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 "route_group_menu.h"
82 #include "ardour/track.h"
86 using namespace ARDOUR;
87 using namespace ARDOUR_UI_UTILS;
89 using namespace Gtkmm2ext;
91 using namespace Editing;
95 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
98 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
100 , parent_canvas (canvas)
102 , button_table (3, 3)
103 , route_group_button (S_("RTAV|G"))
104 , playlist_button (S_("RTAV|P"))
105 , automation_button (S_("RTAV|A"))
106 , automation_action_menu (0)
107 , plugins_submenu_item (0)
108 , route_group_menu (0)
109 , playlist_action_menu (0)
111 , color_mode_menu (0)
112 , gm (sess, true, 75, 14)
113 , _ignore_set_layer_display (false)
114 , gain_automation_item(NULL)
115 , trim_automation_item(NULL)
116 , mute_automation_item(NULL)
117 , pan_automation_item(NULL)
119 number_label.set_name("tracknumber label");
120 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
121 number_label.set_alignment(.5, .5);
122 number_label.set_fallthrough_to_parent (true);
124 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
128 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
130 RouteUI::set_route (rt);
132 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
133 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
134 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
137 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
140 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
141 gm.get_level_meter().set_no_show_all();
142 gm.get_level_meter().setup_meters(50, meter_width);
143 gm.update_gain_sensitive ();
145 string str = gui_property ("height");
147 set_height (atoi (str));
149 set_height (preset_height (HeightNormal));
152 if (!_route->is_auditioner()) {
153 if (gui_property ("visible").empty()) {
154 set_gui_property ("visible", true);
157 set_gui_property ("visible", false);
160 timestretch_rect = 0;
163 ignore_toggle = false;
165 route_group_button.set_name ("route button");
166 playlist_button.set_name ("route button");
167 automation_button.set_name ("route button");
169 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
170 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
171 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
175 if (ARDOUR::Profile->get_mixbus()) {
176 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
178 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
181 if (is_midi_track()) {
182 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
183 gm.set_fader_name ("MidiTrackFader");
185 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
186 gm.set_fader_name ("AudioTrackFader");
189 rec_enable_button->set_sensitive (_session->writable());
191 /* set playlist button tip to the current playlist, and make it update when it changes */
192 update_playlist_tip ();
193 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
196 gm.set_fader_name ("AudioBusFader");
197 Gtk::Fixed *blank = manage(new Gtk::Fixed());
198 controls_button_size_group->add_widget(*blank);
199 if (ARDOUR::Profile->get_mixbus() ) {
200 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
202 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
207 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
209 if (!ARDOUR::Profile->get_mixbus()) {
210 controls_meters_size_group->add_widget (gm.get_level_meter());
213 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
214 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
215 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
216 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
218 if (ARDOUR::Profile->get_mixbus()) {
219 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
221 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223 // mute button is always present, it is used to
224 // force the 'blank' placeholders to the proper size
225 controls_button_size_group->add_widget(*mute_button);
227 if (!_route->is_master()) {
228 if (ARDOUR::Profile->get_mixbus()) {
229 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
231 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 Gtk::Fixed *blank = manage(new Gtk::Fixed());
235 controls_button_size_group->add_widget(*blank);
236 if (ARDOUR::Profile->get_mixbus()) {
237 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
239 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 if (ARDOUR::Profile->get_mixbus()) {
245 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
248 else if (!ARDOUR::Profile->get_trx()) {
249 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
250 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
253 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
254 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
255 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
257 mute_button->set_tweaks(ArdourButton::TrackHeader);
258 solo_button->set_tweaks(ArdourButton::TrackHeader);
259 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
260 playlist_button.set_tweaks(ArdourButton::TrackHeader);
261 automation_button.set_tweaks(ArdourButton::TrackHeader);
262 route_group_button.set_tweaks(ArdourButton::TrackHeader);
264 if (is_midi_track()) {
265 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
267 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
270 update_track_number_visibility();
273 if (ARDOUR::Profile->get_mixbus()) {
274 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
276 else if (!ARDOUR::Profile->get_trx()) {
277 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
280 if (is_track() && track()->mode() == ARDOUR::Normal) {
281 if (ARDOUR::Profile->get_mixbus()) {
282 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
284 else if (!ARDOUR::Profile->get_trx()) {
285 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
291 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
292 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
296 str = gui_property ("layer-display");
298 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
301 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
302 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
304 /* pick up the correct freeze state */
309 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
310 UIConfiguration::ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
312 PropertyList* plist = new PropertyList();
314 plist->add (ARDOUR::Properties::mute, true);
315 plist->add (ARDOUR::Properties::solo, true);
317 route_group_menu = new RouteGroupMenu (_session, plist);
319 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
322 RouteTimeAxisView::~RouteTimeAxisView ()
324 cleanup_gui_properties ();
326 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
330 delete playlist_action_menu;
331 playlist_action_menu = 0;
336 _automation_tracks.clear ();
338 delete route_group_menu;
339 CatchDeletion (this);
343 RouteTimeAxisView::post_construct ()
345 /* map current state of the route */
347 update_diskstream_display ();
348 setup_processor_menu_and_curves ();
349 reset_processor_automation_curves ();
352 /** Set up the processor menu for the current set of processors, and
353 * display automation curves for any parameters which have data.
356 RouteTimeAxisView::setup_processor_menu_and_curves ()
358 _subplugin_menu_map.clear ();
359 subplugin_menu.items().clear ();
360 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
361 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
365 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
367 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
368 if (_route->route_group()) {
369 _route->route_group()->remove (_route);
375 r.push_back (route ());
377 route_group_menu->build (r);
378 route_group_menu->menu()->popup (ev->button, ev->time);
384 RouteTimeAxisView::playlist_changed ()
390 RouteTimeAxisView::label_view ()
392 string x = _route->name ();
393 if (x != name_label.get_text ()) {
394 name_label.set_text (x);
396 const int64_t track_number = _route->track_number ();
397 if (track_number == 0) {
398 number_label.set_text ("");
400 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
405 RouteTimeAxisView::update_track_number_visibility ()
408 bool show_label = _session->config.get_track_name_number();
410 if (_route && _route->is_master()) {
414 if (number_label.get_parent()) {
415 controls_table.remove (number_label);
418 if (ARDOUR::Profile->get_mixbus()) {
419 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
421 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
424 // except the width of the number label is subtracted from the name-hbox, so we
425 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
426 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
428 number_label.set_size_request(tnw, -1);
429 number_label.show ();
430 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
432 number_label.hide ();
433 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
438 RouteTimeAxisView::parameter_changed (string const & p)
440 if (p == "track-name-number") {
441 update_track_number_visibility();
446 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
448 if (what_changed.contains (ARDOUR::Properties::name)) {
454 RouteTimeAxisView::take_name_changed (void *src)
462 RouteTimeAxisView::playlist_click ()
464 build_playlist_menu ();
465 conditionally_add_to_selection ();
466 playlist_action_menu->popup (1, gtk_get_current_event_time());
470 RouteTimeAxisView::automation_click ()
472 conditionally_add_to_selection ();
473 build_automation_action_menu (false);
474 automation_action_menu->popup (1, gtk_get_current_event_time());
478 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
480 using namespace Menu_Helpers;
482 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
483 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
486 detach_menu (subplugin_menu);
488 _main_automation_menu_map.clear ();
489 delete automation_action_menu;
490 automation_action_menu = new Menu;
492 MenuList& items = automation_action_menu->items();
494 automation_action_menu->set_name ("ArdourContextMenu");
496 items.push_back (MenuElem (_("Show All Automation"),
497 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
499 items.push_back (MenuElem (_("Show Existing Automation"),
500 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
502 items.push_back (MenuElem (_("Hide All Automation"),
503 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
505 /* Attach the plugin submenu. It may have previously been used elsewhere,
506 so it was detached above
509 if (!subplugin_menu.items().empty()) {
510 items.push_back (SeparatorElem ());
511 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
512 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
515 /* Add any route automation */
518 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
519 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
520 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
521 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
523 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
527 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
528 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
529 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
530 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
532 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
536 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
537 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
538 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
539 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
541 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
544 if (!pan_tracks.empty()) {
545 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
546 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
547 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
548 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
550 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
551 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
552 _main_automation_menu_map[*p] = pan_automation_item;
558 RouteTimeAxisView::build_display_menu ()
560 using namespace Menu_Helpers;
564 TimeAxisView::build_display_menu ();
566 /* now fill it with our stuff */
568 MenuList& items = display_menu->items();
569 display_menu->set_name ("ArdourContextMenu");
571 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
573 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
575 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
577 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
579 items.push_back (SeparatorElem());
582 detach_menu (*_size_menu);
585 items.push_back (MenuElem (_("Height"), *_size_menu));
587 items.push_back (SeparatorElem());
589 if (!Profile->get_sae()) {
590 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
591 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
592 items.push_back (SeparatorElem());
595 // Hook for derived classes to add type specific stuff
596 append_extra_display_menu_items ();
600 Menu* layers_menu = manage (new Menu);
601 MenuList &layers_items = layers_menu->items();
602 layers_menu->set_name("ArdourContextMenu");
604 RadioMenuItem::Group layers_group;
606 /* Find out how many overlaid/stacked tracks we have in the selection */
610 TrackSelection const & s = _editor.get_selection().tracks;
611 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
612 StreamView* v = (*i)->view ();
617 switch (v->layer_display ()) {
628 /* We're not connecting to signal_toggled() here; in the case where these two items are
629 set to be in the `inconsistent' state, it seems that one or other will end up active
630 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
631 select the active one, no toggled signal is emitted so nothing happens.
634 _ignore_set_layer_display = true;
636 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
637 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
638 i->set_active (overlaid != 0 && stacked == 0);
639 i->set_inconsistent (overlaid != 0 && stacked != 0);
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
642 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
643 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
644 i->set_active (overlaid == 0 && stacked != 0);
645 i->set_inconsistent (overlaid != 0 && stacked != 0);
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
648 _ignore_set_layer_display = false;
650 items.push_back (MenuElem (_("Layers"), *layers_menu));
652 if (!Profile->get_sae()) {
654 Menu* alignment_menu = manage (new Menu);
655 MenuList& alignment_items = alignment_menu->items();
656 alignment_menu->set_name ("ArdourContextMenu");
658 RadioMenuItem::Group align_group;
660 /* Same verbose hacks as for the layering options above */
666 boost::shared_ptr<Track> first_track;
668 TrackSelection const & s = _editor.get_selection().tracks;
669 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
670 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
671 if (!r || !r->is_track ()) {
676 first_track = r->track();
679 switch (r->track()->alignment_choice()) {
683 switch (r->track()->alignment_style()) {
684 case ExistingMaterial:
692 case UseExistingMaterial:
708 inconsistent = false;
717 if (!inconsistent && first_track) {
719 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
720 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
721 i->set_active (automatic != 0 && existing == 0 && capture == 0);
722 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
724 switch (first_track->alignment_choice()) {
726 switch (first_track->alignment_style()) {
727 case ExistingMaterial:
728 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
731 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
739 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
740 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
741 i->set_active (existing != 0 && capture == 0 && automatic == 0);
742 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
744 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
745 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
746 i->set_active (existing == 0 && capture != 0 && automatic == 0);
747 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
749 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
755 Menu* mode_menu = manage (new Menu);
756 MenuList& mode_items = mode_menu->items ();
757 mode_menu->set_name ("ArdourContextMenu");
759 RadioMenuItem::Group mode_group;
765 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
766 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
767 if (!r || !r->is_track ()) {
771 switch (r->track()->mode()) {
784 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
785 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
786 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
787 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
788 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
790 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
791 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
792 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
793 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
794 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
796 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
797 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
798 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
799 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
800 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
802 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 items.push_front (SeparatorElem());
866 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
870 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
872 if (apply_to_selection) {
873 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
876 bool needs_bounce = false;
878 if (!track()->can_use_mode (mode, needs_bounce)) {
884 cerr << "would bounce this one\n";
889 track()->set_mode (mode);
894 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
896 TimeAxisView::show_timestretch (start, end, layers, layer);
906 /* check that the time selection was made in our route, or our route group.
907 remember that route_group() == 0 implies the route is *not* in a edit group.
910 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
911 /* this doesn't apply to us */
915 /* ignore it if our edit group is not active */
917 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
922 if (timestretch_rect == 0) {
923 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
924 timestretch_rect->set_fill_color (ArdourCanvas::HSV (ARDOUR_UI::config()->color ("time stretch fill")).mod (ARDOUR_UI::config()->modifier ("time stretch fill")).color());
925 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
928 timestretch_rect->show ();
929 timestretch_rect->raise_to_top ();
931 double const x1 = start / _editor.get_current_zoom();
932 double const x2 = (end - 1) / _editor.get_current_zoom();
934 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
935 x2, current_height() * (layers - layer) / layers));
939 RouteTimeAxisView::hide_timestretch ()
941 TimeAxisView::hide_timestretch ();
943 if (timestretch_rect) {
944 timestretch_rect->hide ();
949 RouteTimeAxisView::show_selection (TimeSelection& ts)
953 /* ignore it if our edit group is not active or if the selection was started
954 in some other track or route group (remember that route_group() == 0 means
955 that the track is not in an route group).
958 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
959 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
965 TimeAxisView::show_selection (ts);
969 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
972 bool height_changed = (height == 0) || (h != height);
975 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
978 gm.get_level_meter().setup_meters (gmlen, meter_width);
980 TimeAxisView::set_height (h, m);
983 _view->set_height ((double) current_height());
986 if (height >= preset_height (HeightNormal)) {
990 gm.get_gain_slider().show();
992 if (!_route || _route->is_monitor()) {
997 if (rec_enable_button)
998 rec_enable_button->show();
1000 route_group_button.show();
1001 automation_button.show();
1003 if (is_track() && track()->mode() == ARDOUR::Normal) {
1004 playlist_button.show();
1011 gm.get_gain_slider().hide();
1012 mute_button->show();
1013 if (!_route || _route->is_monitor()) {
1014 solo_button->hide();
1016 solo_button->show();
1018 if (rec_enable_button)
1019 rec_enable_button->show();
1021 route_group_button.hide ();
1022 automation_button.hide ();
1024 if (is_track() && track()->mode() == ARDOUR::Normal) {
1025 playlist_button.hide ();
1030 if (height_changed && !no_redraw) {
1031 /* only emit the signal if the height really changed */
1037 RouteTimeAxisView::route_color_changed ()
1040 _view->apply_color (color(), StreamView::RegionColor);
1043 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1047 RouteTimeAxisView::reset_samples_per_pixel ()
1049 set_samples_per_pixel (_editor.get_current_zoom());
1053 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1058 speed = track()->speed();
1062 _view->set_samples_per_pixel (fpp * speed);
1065 TimeAxisView::set_samples_per_pixel (fpp * speed);
1069 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1071 if (!mitem->get_active()) {
1072 /* this is one of the two calls made when these radio menu items change status. this one
1073 is for the item that became inactive, and we want to ignore it.
1078 if (apply_to_selection) {
1079 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1082 track()->set_align_choice (choice);
1088 RouteTimeAxisView::rename_current_playlist ()
1090 ArdourPrompter prompter (true);
1093 boost::shared_ptr<Track> tr = track();
1094 if (!tr || tr->destructive()) {
1098 boost::shared_ptr<Playlist> pl = tr->playlist();
1103 prompter.set_title (_("Rename Playlist"));
1104 prompter.set_prompt (_("New name for playlist:"));
1105 prompter.set_initial_text (pl->name());
1106 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1107 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1109 switch (prompter.run ()) {
1110 case Gtk::RESPONSE_ACCEPT:
1111 prompter.get_result (name);
1112 if (name.length()) {
1113 pl->set_name (name);
1123 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1125 std::string ret (basename);
1127 std::string const group_string = "." + route_group()->name() + ".";
1129 // iterate through all playlists
1131 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1132 std::string tmp = (*i)->name();
1134 std::string::size_type idx = tmp.find(group_string);
1135 // find those which belong to this group
1136 if (idx != string::npos) {
1137 tmp = tmp.substr(idx + group_string.length());
1139 // and find the largest current number
1141 if (x > maxnumber) {
1150 snprintf (buf, sizeof(buf), "%d", maxnumber);
1152 ret = this->name() + "." + route_group()->name () + "." + buf;
1158 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1162 boost::shared_ptr<Track> tr = track ();
1163 if (!tr || tr->destructive()) {
1167 boost::shared_ptr<const Playlist> pl = tr->playlist();
1174 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1175 name = resolve_new_group_playlist_name(name, playlists_before_op);
1178 while (_session->playlists->by_name(name)) {
1179 name = Playlist::bump_name (name, *_session);
1182 // TODO: The prompter "new" button should be de-activated if the user
1183 // specifies a playlist name which already exists in the session.
1187 ArdourPrompter prompter (true);
1189 prompter.set_title (_("New Copy Playlist"));
1190 prompter.set_prompt (_("Name for new playlist:"));
1191 prompter.set_initial_text (name);
1192 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1193 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1194 prompter.show_all ();
1196 switch (prompter.run ()) {
1197 case Gtk::RESPONSE_ACCEPT:
1198 prompter.get_result (name);
1206 if (name.length()) {
1207 tr->use_copy_playlist ();
1208 tr->playlist()->set_name (name);
1213 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1217 boost::shared_ptr<Track> tr = track ();
1218 if (!tr || tr->destructive()) {
1222 boost::shared_ptr<const Playlist> pl = tr->playlist();
1229 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1230 name = resolve_new_group_playlist_name(name,playlists_before_op);
1233 while (_session->playlists->by_name(name)) {
1234 name = Playlist::bump_name (name, *_session);
1240 ArdourPrompter prompter (true);
1242 prompter.set_title (_("New Playlist"));
1243 prompter.set_prompt (_("Name for new playlist:"));
1244 prompter.set_initial_text (name);
1245 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1246 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1248 switch (prompter.run ()) {
1249 case Gtk::RESPONSE_ACCEPT:
1250 prompter.get_result (name);
1258 if (name.length()) {
1259 tr->use_new_playlist ();
1260 tr->playlist()->set_name (name);
1265 RouteTimeAxisView::clear_playlist ()
1267 boost::shared_ptr<Track> tr = track ();
1268 if (!tr || tr->destructive()) {
1272 boost::shared_ptr<Playlist> pl = tr->playlist();
1277 _editor.clear_playlist (pl);
1281 RouteTimeAxisView::speed_changed ()
1283 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1287 RouteTimeAxisView::update_diskstream_display ()
1297 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1299 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1301 /* special case: select/deselect all tracks */
1303 _editor.begin_reversible_selection_op (X_("Selection Click"));
1305 if (_editor.get_selection().selected (this)) {
1306 _editor.get_selection().clear_tracks ();
1308 _editor.select_all_tracks ();
1311 _editor.commit_reversible_selection_op ();
1316 _editor.begin_reversible_selection_op (X_("Selection Click"));
1318 switch (ArdourKeyboard::selection_type (ev->state)) {
1319 case Selection::Toggle:
1320 _editor.get_selection().toggle (this);
1323 case Selection::Set:
1324 _editor.get_selection().set (this);
1327 case Selection::Extend:
1328 _editor.extend_selection_to_track (*this);
1331 case Selection::Add:
1332 _editor.get_selection().add (this);
1336 _editor.commit_reversible_selection_op ();
1340 RouteTimeAxisView::set_selected_points (PointSelection& points)
1342 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1343 (*i)->set_selected_points (points);
1345 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1347 asv->set_selected_points (points);
1352 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1355 _view->set_selected_regionviews (regions);
1359 /** Add the selectable things that we have to a list.
1360 * @param results List to add things to.
1363 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1368 speed = track()->speed();
1371 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1372 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1374 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1375 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1378 /* pick up visible automation tracks */
1380 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1381 if (!(*i)->hidden()) {
1382 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1388 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1391 _view->get_inverted_selectables (sel, results);
1394 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1395 if (!(*i)->hidden()) {
1396 (*i)->get_inverted_selectables (sel, results);
1404 RouteTimeAxisView::route_group () const
1406 return _route->route_group();
1410 RouteTimeAxisView::name() const
1412 return _route->name();
1415 boost::shared_ptr<Playlist>
1416 RouteTimeAxisView::playlist () const
1418 boost::shared_ptr<Track> tr;
1420 if ((tr = track()) != 0) {
1421 return tr->playlist();
1423 return boost::shared_ptr<Playlist> ();
1428 RouteTimeAxisView::name_entry_changed ()
1430 TimeAxisView::name_entry_changed ();
1432 string x = name_entry->get_text ();
1434 if (x == _route->name()) {
1438 strip_whitespace_edges (x);
1440 if (x.length() == 0) {
1441 name_entry->set_text (_route->name());
1445 if (_session->route_name_internal (x)) {
1446 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1448 name_entry->grab_focus ();
1449 } else if (RouteUI::verify_new_route_name (x)) {
1450 _route->set_name (x);
1452 name_entry->grab_focus ();
1456 boost::shared_ptr<Region>
1457 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1459 boost::shared_ptr<Playlist> pl = playlist ();
1462 return pl->find_next_region (pos, point, dir);
1465 return boost::shared_ptr<Region> ();
1469 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1471 boost::shared_ptr<Playlist> pl = playlist ();
1474 return pl->find_next_region_boundary (pos, dir);
1481 RouteTimeAxisView::fade_range (TimeSelection& selection)
1483 boost::shared_ptr<Playlist> what_we_got;
1484 boost::shared_ptr<Track> tr = track ();
1485 boost::shared_ptr<Playlist> playlist;
1488 /* route is a bus, not a track */
1492 playlist = tr->playlist();
1494 TimeSelection time (selection);
1495 float const speed = tr->speed();
1496 if (speed != 1.0f) {
1497 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1498 (*i).start = session_frame_to_track_frame((*i).start, speed);
1499 (*i).end = session_frame_to_track_frame((*i).end, speed);
1503 playlist->clear_changes ();
1504 playlist->clear_owned_changes ();
1506 playlist->fade_range (time);
1508 vector<Command*> cmds;
1509 playlist->rdiff (cmds);
1510 _session->add_commands (cmds);
1511 _session->add_command (new StatefulDiffCommand (playlist));
1516 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1518 boost::shared_ptr<Playlist> what_we_got;
1519 boost::shared_ptr<Track> tr = track ();
1520 boost::shared_ptr<Playlist> playlist;
1523 /* route is a bus, not a track */
1527 playlist = tr->playlist();
1529 TimeSelection time (selection.time);
1530 float const speed = tr->speed();
1531 if (speed != 1.0f) {
1532 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1533 (*i).start = session_frame_to_track_frame((*i).start, speed);
1534 (*i).end = session_frame_to_track_frame((*i).end, speed);
1538 playlist->clear_changes ();
1539 playlist->clear_owned_changes ();
1543 if (playlist->cut (time) != 0) {
1544 if (Config->get_edit_mode() == Ripple)
1545 playlist->ripple(time.start(), -time.length(), NULL);
1546 // no need to exclude any regions from rippling here
1548 vector<Command*> cmds;
1549 playlist->rdiff (cmds);
1550 _session->add_commands (cmds);
1552 _session->add_command (new StatefulDiffCommand (playlist));
1557 if ((what_we_got = playlist->cut (time)) != 0) {
1558 _editor.get_cut_buffer().add (what_we_got);
1559 if (Config->get_edit_mode() == Ripple)
1560 playlist->ripple(time.start(), -time.length(), NULL);
1561 // no need to exclude any regions from rippling here
1563 vector<Command*> cmds;
1564 playlist->rdiff (cmds);
1565 _session->add_commands (cmds);
1567 _session->add_command (new StatefulDiffCommand (playlist));
1571 if ((what_we_got = playlist->copy (time)) != 0) {
1572 _editor.get_cut_buffer().add (what_we_got);
1577 if ((what_we_got = playlist->cut (time)) != 0) {
1578 if (Config->get_edit_mode() == Ripple)
1579 playlist->ripple(time.start(), -time.length(), NULL);
1580 // no need to exclude any regions from rippling here
1582 vector<Command*> cmds;
1583 playlist->rdiff (cmds);
1584 _session->add_commands (cmds);
1585 _session->add_command (new StatefulDiffCommand (playlist));
1586 what_we_got->release ();
1593 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1599 boost::shared_ptr<Playlist> pl = playlist ();
1600 const ARDOUR::DataType type = pl->data_type();
1601 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1603 if (p == selection.playlists.end()) {
1606 ctx.counts.increase_n_playlists(type);
1608 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1610 if (track()->speed() != 1.0f) {
1611 pos = session_frame_to_track_frame (pos, track()->speed());
1612 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1615 /* add multi-paste offset if applicable */
1616 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1617 const framecnt_t duration = extent.second - extent.first;
1618 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1620 pl->clear_changes ();
1621 pl->clear_owned_changes ();
1622 if (Config->get_edit_mode() == Ripple) {
1623 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1624 framecnt_t amount = extent.second - extent.first;
1625 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1627 pl->paste (*p, pos, ctx.times);
1629 vector<Command*> cmds;
1631 _session->add_commands (cmds);
1633 _session->add_command (new StatefulDiffCommand (pl));
1639 struct PlaylistSorter {
1640 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1641 return a->sort_id() < b->sort_id();
1646 RouteTimeAxisView::build_playlist_menu ()
1648 using namespace Menu_Helpers;
1654 delete playlist_action_menu;
1655 playlist_action_menu = new Menu;
1656 playlist_action_menu->set_name ("ArdourContextMenu");
1658 MenuList& playlist_items = playlist_action_menu->items();
1659 playlist_action_menu->set_name ("ArdourContextMenu");
1660 playlist_items.clear();
1662 RadioMenuItem::Group playlist_group;
1663 boost::shared_ptr<Track> tr = track ();
1665 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1667 /* sort the playlists */
1669 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1671 /* add the playlists to the menu */
1672 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1673 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1674 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1675 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1677 if (tr->playlist()->id() == (*i)->id()) {
1683 playlist_items.push_back (SeparatorElem());
1684 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1685 playlist_items.push_back (SeparatorElem());
1687 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1688 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1689 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1692 // Use a label which tells the user what is happening
1693 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1694 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1698 playlist_items.push_back (SeparatorElem());
1699 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1700 playlist_items.push_back (SeparatorElem());
1702 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1706 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1708 assert (is_track());
1710 // exit if we were triggered by deactivating the old playlist
1711 if (!item->get_active()) {
1715 boost::shared_ptr<Playlist> pl (wpl.lock());
1721 if (track()->playlist() == pl) {
1722 // exit when use_playlist is called by the creation of the playlist menu
1723 // or the playlist choice is unchanged
1727 track()->use_playlist (pl);
1729 RouteGroup* rg = route_group();
1731 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1732 std::string group_string = "." + rg->name() + ".";
1734 std::string take_name = pl->name();
1735 std::string::size_type idx = take_name.find(group_string);
1737 if (idx == std::string::npos)
1740 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1742 boost::shared_ptr<RouteList> rl (rg->route_list());
1744 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1745 if ((*i) == this->route()) {
1749 std::string playlist_name = (*i)->name()+group_string+take_name;
1751 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1756 if (track->freeze_state() == Track::Frozen) {
1757 /* Don't change playlists of frozen tracks */
1761 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1763 // No playlist for this track for this take yet, make it
1764 track->use_new_playlist();
1765 track->playlist()->set_name(playlist_name);
1767 track->use_playlist(ipl);
1774 RouteTimeAxisView::update_playlist_tip ()
1776 RouteGroup* rg = route_group ();
1777 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1778 string group_string = "." + rg->name() + ".";
1780 string take_name = track()->playlist()->name();
1781 string::size_type idx = take_name.find(group_string);
1783 if (idx != string::npos) {
1784 /* find the bit containing the take number / name */
1785 take_name = take_name.substr (idx + group_string.length());
1787 /* set the playlist button tooltip to the take name */
1788 ARDOUR_UI::instance()->set_tip (
1790 string_compose(_("Take: %1.%2"),
1791 Glib::Markup::escape_text(rg->name()),
1792 Glib::Markup::escape_text(take_name))
1799 /* set the playlist button tooltip to the playlist name */
1800 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1805 RouteTimeAxisView::show_playlist_selector ()
1807 _editor.playlist_selector().show_for (this);
1811 RouteTimeAxisView::map_frozen ()
1817 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1819 switch (track()->freeze_state()) {
1821 playlist_button.set_sensitive (false);
1822 rec_enable_button->set_sensitive (false);
1825 playlist_button.set_sensitive (true);
1826 rec_enable_button->set_sensitive (true);
1832 RouteTimeAxisView::color_handler ()
1834 //case cTimeStretchOutline:
1835 if (timestretch_rect) {
1836 timestretch_rect->set_outline_color (ARDOUR_UI::config()->color ("time stretch outline"));
1838 //case cTimeStretchFill:
1839 if (timestretch_rect) {
1840 timestretch_rect->set_fill_color (ARDOUR_UI::config()->color ("time stretch fill"));
1846 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1847 * Will add track if necessary.
1850 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1852 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1853 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1856 /* it doesn't exist yet, so we don't care about the button state: just add it */
1857 create_automation_child (param, true);
1860 bool yn = menu->get_active();
1861 bool changed = false;
1863 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1865 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1866 will have done that for us.
1869 if (changed && !no_redraw) {
1877 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1879 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1885 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1887 if (menu && !_hidden) {
1888 ignore_toggle = true;
1889 menu->set_active (false);
1890 ignore_toggle = false;
1893 if (_route && !no_redraw) {
1899 RouteTimeAxisView::update_gain_track_visibility ()
1901 bool const showit = gain_automation_item->get_active();
1903 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1904 gain_track->set_marked_for_display (showit);
1906 /* now trigger a redisplay */
1909 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1915 RouteTimeAxisView::update_trim_track_visibility ()
1917 bool const showit = trim_automation_item->get_active();
1919 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1920 trim_track->set_marked_for_display (showit);
1922 /* now trigger a redisplay */
1925 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1931 RouteTimeAxisView::update_mute_track_visibility ()
1933 bool const showit = mute_automation_item->get_active();
1935 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1936 mute_track->set_marked_for_display (showit);
1938 /* now trigger a redisplay */
1941 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1947 RouteTimeAxisView::update_pan_track_visibility ()
1949 bool const showit = pan_automation_item->get_active();
1950 bool changed = false;
1952 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1953 if ((*i)->set_marked_for_display (showit)) {
1959 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1964 RouteTimeAxisView::ensure_pan_views (bool show)
1966 bool changed = false;
1967 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1969 (*i)->set_marked_for_display (false);
1972 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1976 if (!_route->panner()) {
1980 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1981 set<Evoral::Parameter>::iterator p;
1983 for (p = params.begin(); p != params.end(); ++p) {
1984 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1986 if (pan_control->parameter().type() == NullAutomation) {
1987 error << "Pan control has NULL automation type!" << endmsg;
1991 if (automation_child (pan_control->parameter ()).get () == 0) {
1993 /* we don't already have an AutomationTimeAxisView for this parameter */
1995 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1997 boost::shared_ptr<AutomationTimeAxisView> t (
1998 new AutomationTimeAxisView (_session,
2002 pan_control->parameter (),
2010 pan_tracks.push_back (t);
2011 add_automation_child (*p, t, show);
2013 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2020 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2022 if (apply_to_selection) {
2023 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2027 /* Show our automation */
2029 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2030 i->second->set_marked_for_display (true);
2032 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2035 menu->set_active(true);
2040 /* Show processor automation */
2042 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2043 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2044 if ((*ii)->view == 0) {
2045 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2048 (*ii)->menu_item->set_active (true);
2061 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2063 if (apply_to_selection) {
2064 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2068 /* Show our automation */
2070 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2071 if (i->second->has_automation()) {
2072 i->second->set_marked_for_display (true);
2074 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2076 menu->set_active(true);
2081 /* Show processor automation */
2083 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2084 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2085 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2086 (*ii)->menu_item->set_active (true);
2098 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2100 if (apply_to_selection) {
2101 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2105 /* Hide our automation */
2107 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2108 i->second->set_marked_for_display (false);
2110 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2113 menu->set_active (false);
2117 /* Hide processor automation */
2119 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2120 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2121 (*ii)->menu_item->set_active (false);
2132 RouteTimeAxisView::region_view_added (RegionView* rv)
2134 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2135 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2136 boost::shared_ptr<AutomationTimeAxisView> atv;
2138 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2143 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2144 (*i)->add_ghost(rv);
2148 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2150 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2156 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2158 parent.remove_processor_automation_node (this);
2162 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2165 remove_child (pan->view);
2169 RouteTimeAxisView::ProcessorAutomationNode*
2170 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2172 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2174 if ((*i)->processor == processor) {
2176 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2177 if ((*ii)->what == what) {
2187 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2189 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2192 ProcessorAutomationNode* pan;
2194 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2195 /* session state may never have been saved with new plugin */
2196 error << _("programming error: ")
2197 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2198 processor->name(), what.type(), (int) what.channel(), what.id() )
2200 abort(); /*NOTREACHED*/
2208 boost::shared_ptr<AutomationControl> control
2209 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2211 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2212 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2213 _editor, *this, false, parent_canvas,
2214 processor->describe_parameter (what), processor->name()));
2216 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2218 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2221 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2226 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2229 pan->menu_item->set_active (false);
2238 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2240 boost::shared_ptr<Processor> processor (p.lock ());
2242 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2243 /* The Amp processor is a special case and is dealt with separately */
2247 set<Evoral::Parameter> existing;
2249 processor->what_has_data (existing);
2251 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2253 Evoral::Parameter param (*i);
2254 boost::shared_ptr<AutomationLine> al;
2256 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2259 add_processor_automation_curve (processor, param);
2265 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2267 using namespace Menu_Helpers;
2271 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2273 _automation_tracks[param] = track;
2275 /* existing state overrides "show" argument */
2276 string s = track->gui_property ("visible");
2278 show = string_is_affirmative (s);
2281 /* this might or might not change the visibility status, so don't rely on it */
2282 track->set_marked_for_display (show);
2284 if (show && !no_redraw) {
2288 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2289 /* MIDI-related parameters are always in the menu, there's no
2290 reason to rebuild the menu just because we added a automation
2291 lane for one of them. But if we add a non-MIDI automation
2292 lane, then we need to invalidate the display menu.
2294 delete display_menu;
2300 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2302 boost::shared_ptr<Processor> processor (p.lock ());
2304 if (!processor || !processor->display_to_user ()) {
2308 /* we use this override to veto the Amp processor from the plugin menu,
2309 as its automation lane can be accessed using the special "Fader" menu
2313 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2317 using namespace Menu_Helpers;
2318 ProcessorAutomationInfo *rai;
2319 list<ProcessorAutomationInfo*>::iterator x;
2321 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2323 if (automatable.empty()) {
2327 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2328 if ((*x)->processor == processor) {
2333 if (x == processor_automation.end()) {
2335 rai = new ProcessorAutomationInfo (processor);
2336 processor_automation.push_back (rai);
2344 /* any older menu was deleted at the top of processors_changed()
2345 when we cleared the subplugin menu.
2348 rai->menu = manage (new Menu);
2349 MenuList& items = rai->menu->items();
2350 rai->menu->set_name ("ArdourContextMenu");
2354 std::set<Evoral::Parameter> has_visible_automation;
2355 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2357 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2359 ProcessorAutomationNode* pan;
2360 Gtk::CheckMenuItem* mitem;
2362 string name = processor->describe_parameter (*i);
2364 items.push_back (CheckMenuElem (name));
2365 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2367 _subplugin_menu_map[*i] = mitem;
2369 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2370 mitem->set_active(true);
2373 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2377 pan = new ProcessorAutomationNode (*i, mitem, *this);
2379 rai->lines.push_back (pan);
2383 pan->menu_item = mitem;
2387 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2390 /* add the menu for this processor, because the subplugin
2391 menu is always cleared at the top of processors_changed().
2392 this is the result of some poor design in gtkmm and/or
2396 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2401 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2402 RouteTimeAxisView::ProcessorAutomationNode* pan)
2404 bool showit = pan->menu_item->get_active();
2405 bool redraw = false;
2407 if (pan->view == 0 && showit) {
2408 add_processor_automation_curve (rai->processor, pan->what);
2412 if (pan->view && pan->view->set_marked_for_display (showit)) {
2416 if (redraw && !no_redraw) {
2422 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2424 if (c.type == RouteProcessorChange::MeterPointChange) {
2425 /* nothing to do if only the meter point has changed */
2429 using namespace Menu_Helpers;
2431 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2432 (*i)->valid = false;
2435 setup_processor_menu_and_curves ();
2437 bool deleted_processor_automation = false;
2439 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2441 list<ProcessorAutomationInfo*>::iterator tmp;
2449 processor_automation.erase (i);
2450 deleted_processor_automation = true;
2457 if (deleted_processor_automation && !no_redraw) {
2462 boost::shared_ptr<AutomationLine>
2463 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2465 ProcessorAutomationNode* pan;
2467 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2473 return boost::shared_ptr<AutomationLine>();
2477 RouteTimeAxisView::reset_processor_automation_curves ()
2479 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2485 RouteTimeAxisView::can_edit_name () const
2487 /* we do not allow track name changes if it is record enabled
2489 return !_route->record_enabled();
2493 RouteTimeAxisView::blink_rec_display (bool onoff)
2495 RouteUI::blink_rec_display (onoff);
2499 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2501 if (_ignore_set_layer_display) {
2505 if (apply_to_selection) {
2506 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2510 _view->set_layer_display (d);
2513 set_gui_property (X_("layer-display"), enum_2_string (d));
2518 RouteTimeAxisView::layer_display () const
2521 return _view->layer_display ();
2524 /* we don't know, since we don't have a _view, so just return something */
2530 boost::shared_ptr<AutomationTimeAxisView>
2531 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2533 AutomationTracks::iterator i = _automation_tracks.find(param);
2534 if (i != _automation_tracks.end()) {
2537 return boost::shared_ptr<AutomationTimeAxisView>();
2542 RouteTimeAxisView::fast_update ()
2544 gm.get_level_meter().update_meters ();
2548 RouteTimeAxisView::hide_meter ()
2551 gm.get_level_meter().hide_meters ();
2555 RouteTimeAxisView::show_meter ()
2561 RouteTimeAxisView::reset_meter ()
2563 if (ARDOUR_UI::config()->get_show_track_meters()) {
2564 int meter_width = 3;
2565 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2568 gm.get_level_meter().setup_meters (height - 9, meter_width);
2575 RouteTimeAxisView::clear_meter ()
2577 gm.get_level_meter().clear_meters ();
2581 RouteTimeAxisView::meter_changed ()
2583 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2585 if (_route && !no_redraw && ARDOUR_UI::config()->get_show_track_meters()) {
2588 // reset peak when meter point changes
2589 gm.reset_peak_display();
2593 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2596 if (_route && !no_redraw) {
2602 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2604 using namespace Menu_Helpers;
2606 if (!_underlay_streams.empty()) {
2607 MenuList& parent_items = parent_menu->items();
2608 Menu* gs_menu = manage (new Menu);
2609 gs_menu->set_name ("ArdourContextMenu");
2610 MenuList& gs_items = gs_menu->items();
2612 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2614 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2615 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2616 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2622 RouteTimeAxisView::set_underlay_state()
2624 if (!underlay_xml_node) {
2628 XMLNodeList nlist = underlay_xml_node->children();
2629 XMLNodeConstIterator niter;
2630 XMLNode *child_node;
2632 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2633 child_node = *niter;
2635 if (child_node->name() != "Underlay") {
2639 XMLProperty* prop = child_node->property ("id");
2641 PBD::ID id (prop->value());
2643 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2646 add_underlay(v->view(), false);
2655 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2661 RouteTimeAxisView& other = v->trackview();
2663 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2664 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2665 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2666 abort(); /*NOTREACHED*/
2669 _underlay_streams.push_back(v);
2670 other._underlay_mirrors.push_back(this);
2672 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2674 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2676 if (!underlay_xml_node) {
2677 underlay_xml_node = xml_node->add_child("Underlays");
2680 XMLNode* node = underlay_xml_node->add_child("Underlay");
2681 XMLProperty* prop = node->add_property("id");
2682 prop->set_value(v->trackview().route()->id().to_s());
2689 RouteTimeAxisView::remove_underlay (StreamView* v)
2695 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2696 RouteTimeAxisView& other = v->trackview();
2698 if (it != _underlay_streams.end()) {
2699 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2701 if (gm == other._underlay_mirrors.end()) {
2702 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2703 abort(); /*NOTREACHED*/
2706 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2708 _underlay_streams.erase(it);
2709 other._underlay_mirrors.erase(gm);
2711 if (underlay_xml_node) {
2712 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2718 RouteTimeAxisView::set_button_names ()
2720 if (_route && _route->solo_safe()) {
2721 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2723 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2725 if (Config->get_solo_control_is_listen_control()) {
2726 switch (Config->get_listen_position()) {
2727 case AfterFaderListen:
2728 solo_button->set_text (S_("AfterFader|A"));
2729 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2731 case PreFaderListen:
2732 solo_button->set_text (S_("PreFader|P"));
2733 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2737 solo_button->set_text (S_("Solo|S"));
2738 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2740 mute_button->set_text (S_("Mute|M"));
2744 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2746 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2747 if (i != _main_automation_menu_map.end()) {
2751 i = _subplugin_menu_map.find (param);
2752 if (i != _subplugin_menu_map.end()) {
2760 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2762 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2764 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2768 gain_track.reset (new AutomationTimeAxisView (_session,
2769 _route, _route->amp(), c, param,
2774 _route->amp()->describe_parameter(param)));
2777 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2780 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2784 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2786 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2787 if (!c || ! _route->trim()->active()) {
2791 trim_track.reset (new AutomationTimeAxisView (_session,
2792 _route, _route->trim(), c, param,
2797 _route->trim()->describe_parameter(param)));
2800 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2803 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2807 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2809 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2811 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2815 mute_track.reset (new AutomationTimeAxisView (_session,
2816 _route, _route, c, param,
2821 _route->describe_parameter(param)));
2824 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2827 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2831 void add_region_to_list (RegionView* rv, RegionList* l)
2833 l->push_back (rv->region());
2837 RouteTimeAxisView::combine_regions ()
2839 /* as of may 2011, we do not offer uncombine for MIDI tracks
2842 if (!is_audio_track()) {
2850 RegionList selected_regions;
2851 boost::shared_ptr<Playlist> playlist = track()->playlist();
2853 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2855 if (selected_regions.size() < 2) {
2859 playlist->clear_changes ();
2860 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2862 _session->add_command (new StatefulDiffCommand (playlist));
2863 /* make the new region be selected */
2865 return _view->find_view (compound_region);
2869 RouteTimeAxisView::uncombine_regions ()
2871 /* as of may 2011, we do not offer uncombine for MIDI tracks
2873 if (!is_audio_track()) {
2881 RegionList selected_regions;
2882 boost::shared_ptr<Playlist> playlist = track()->playlist();
2884 /* have to grab selected regions first because the uncombine is going
2885 * to change that in the middle of the list traverse
2888 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2890 playlist->clear_changes ();
2892 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2893 playlist->uncombine (*i);
2896 _session->add_command (new StatefulDiffCommand (playlist));
2900 RouteTimeAxisView::state_id() const
2902 return string_compose ("rtav %1", _route->id().to_s());
2907 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2909 TimeAxisView::remove_child (c);
2911 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2913 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2914 if (i->second == a) {
2915 _automation_tracks.erase (i);