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());
130 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
132 RouteUI::set_route (rt);
134 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
135 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
136 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
139 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
142 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
143 gm.get_level_meter().set_no_show_all();
144 gm.get_level_meter().setup_meters(50, meter_width);
145 gm.update_gain_sensitive ();
147 string str = gui_property ("height");
149 set_height (atoi (str));
151 set_height (preset_height (HeightNormal));
154 if (!_route->is_auditioner()) {
155 if (gui_property ("visible").empty()) {
156 set_gui_property ("visible", true);
159 set_gui_property ("visible", false);
162 timestretch_rect = 0;
165 ignore_toggle = false;
167 route_group_button.set_name ("route button");
168 playlist_button.set_name ("route button");
169 automation_button.set_name ("route button");
171 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
172 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
173 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
177 if (ARDOUR::Profile->get_mixbus()) {
178 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
180 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
183 if (is_midi_track()) {
184 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 set_tooltip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
199 Gtk::Fixed *blank = manage(new Gtk::Fixed());
200 controls_button_size_group->add_widget(*blank);
201 if (ARDOUR::Profile->get_mixbus() ) {
202 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
211 if (!ARDOUR::Profile->get_mixbus()) {
212 controls_meters_size_group->add_widget (gm.get_level_meter());
215 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
216 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
217 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
218 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
220 if (ARDOUR::Profile->get_mixbus()) {
221 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
223 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
225 // mute button is always present, it is used to
226 // force the 'blank' placeholders to the proper size
227 controls_button_size_group->add_widget(*mute_button);
229 if (!_route->is_master()) {
230 if (ARDOUR::Profile->get_mixbus()) {
231 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
233 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 Gtk::Fixed *blank = manage(new Gtk::Fixed());
237 controls_button_size_group->add_widget(*blank);
238 if (ARDOUR::Profile->get_mixbus()) {
239 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
241 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
246 if (ARDOUR::Profile->get_mixbus()) {
247 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
248 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
250 else if (!ARDOUR::Profile->get_trx()) {
251 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
255 set_tooltip(*solo_button,_("Solo"));
256 set_tooltip(*mute_button,_("Mute"));
257 set_tooltip(route_group_button, _("Route Group"));
259 mute_button->set_tweaks(ArdourButton::TrackHeader);
260 solo_button->set_tweaks(ArdourButton::TrackHeader);
261 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
262 playlist_button.set_tweaks(ArdourButton::TrackHeader);
263 automation_button.set_tweaks(ArdourButton::TrackHeader);
264 route_group_button.set_tweaks(ArdourButton::TrackHeader);
266 if (is_midi_track()) {
267 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
269 set_tooltip(automation_button, _("Automation"));
272 update_track_number_visibility();
275 if (ARDOUR::Profile->get_mixbus()) {
276 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
278 else if (!ARDOUR::Profile->get_trx()) {
279 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
282 if (is_track() && track()->mode() == ARDOUR::Normal) {
283 if (ARDOUR::Profile->get_mixbus()) {
284 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
286 else if (!ARDOUR::Profile->get_trx()) {
287 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
294 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
298 str = gui_property ("layer-display");
300 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
303 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
304 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
306 /* pick up the correct freeze state */
311 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
312 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
314 PropertyList* plist = new PropertyList();
316 plist->add (ARDOUR::Properties::mute, true);
317 plist->add (ARDOUR::Properties::solo, true);
319 route_group_menu = new RouteGroupMenu (_session, plist);
321 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
324 RouteTimeAxisView::~RouteTimeAxisView ()
326 cleanup_gui_properties ();
328 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
332 delete playlist_action_menu;
333 playlist_action_menu = 0;
338 _automation_tracks.clear ();
340 delete route_group_menu;
341 CatchDeletion (this);
345 RouteTimeAxisView::post_construct ()
347 /* map current state of the route */
349 update_diskstream_display ();
350 setup_processor_menu_and_curves ();
351 reset_processor_automation_curves ();
354 /** Set up the processor menu for the current set of processors, and
355 * display automation curves for any parameters which have data.
358 RouteTimeAxisView::setup_processor_menu_and_curves ()
360 _subplugin_menu_map.clear ();
361 subplugin_menu.items().clear ();
362 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
363 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
367 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
369 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
370 if (_route->route_group()) {
371 _route->route_group()->remove (_route);
377 r.push_back (route ());
379 route_group_menu->build (r);
380 route_group_menu->menu()->popup (ev->button, ev->time);
386 RouteTimeAxisView::playlist_changed ()
392 RouteTimeAxisView::label_view ()
394 string x = _route->name ();
395 if (x != name_label.get_text ()) {
396 name_label.set_text (x);
398 const int64_t track_number = _route->track_number ();
399 if (track_number == 0) {
400 number_label.set_text ("");
402 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
407 RouteTimeAxisView::update_track_number_visibility ()
410 bool show_label = _session->config.get_track_name_number();
412 if (_route && _route->is_master()) {
416 if (number_label.get_parent()) {
417 controls_table.remove (number_label);
420 if (ARDOUR::Profile->get_mixbus()) {
421 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
423 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
425 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
426 // except the width of the number label is subtracted from the name-hbox, so we
427 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
428 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
430 number_label.set_size_request(tnw, -1);
431 number_label.show ();
432 name_hbox.set_size_request(TimeAxisView::name_width_px - 2 - tnw, -1); // -2 = cellspacing
434 number_label.hide ();
435 name_hbox.set_size_request(TimeAxisView::name_width_px, -1);
440 RouteTimeAxisView::parameter_changed (string const & p)
442 if (p == "track-name-number") {
443 update_track_number_visibility();
448 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
450 if (what_changed.contains (ARDOUR::Properties::name)) {
456 RouteTimeAxisView::take_name_changed (void *src)
464 RouteTimeAxisView::playlist_click ()
466 build_playlist_menu ();
467 conditionally_add_to_selection ();
468 playlist_action_menu->popup (1, gtk_get_current_event_time());
472 RouteTimeAxisView::automation_click ()
474 conditionally_add_to_selection ();
475 build_automation_action_menu (false);
476 automation_action_menu->popup (1, gtk_get_current_event_time());
480 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
482 using namespace Menu_Helpers;
484 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
485 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
488 detach_menu (subplugin_menu);
490 _main_automation_menu_map.clear ();
491 delete automation_action_menu;
492 automation_action_menu = new Menu;
494 MenuList& items = automation_action_menu->items();
496 automation_action_menu->set_name ("ArdourContextMenu");
498 items.push_back (MenuElem (_("Show All Automation"),
499 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
501 items.push_back (MenuElem (_("Show Existing Automation"),
502 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
504 items.push_back (MenuElem (_("Hide All Automation"),
505 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
507 /* Attach the plugin submenu. It may have previously been used elsewhere,
508 so it was detached above
511 if (!subplugin_menu.items().empty()) {
512 items.push_back (SeparatorElem ());
513 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
514 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
517 /* Add any route automation */
520 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
521 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
522 gain_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
523 (gain_track && string_is_affirmative (gain_track->gui_property ("visible"))));
525 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
529 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
530 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
531 trim_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
532 (trim_track && string_is_affirmative (trim_track->gui_property ("visible"))));
534 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
538 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
539 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
540 mute_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
541 (mute_track && string_is_affirmative (mute_track->gui_property ("visible"))));
543 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
546 if (!pan_tracks.empty()) {
547 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
548 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
549 pan_automation_item->set_active ((!for_selection || _editor.get_selection().tracks.size() == 1) &&
550 (!pan_tracks.empty() && string_is_affirmative (pan_tracks.front()->gui_property ("visible"))));
552 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
553 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
554 _main_automation_menu_map[*p] = pan_automation_item;
560 RouteTimeAxisView::build_display_menu ()
562 using namespace Menu_Helpers;
566 TimeAxisView::build_display_menu ();
568 /* now fill it with our stuff */
570 MenuList& items = display_menu->items();
571 display_menu->set_name ("ArdourContextMenu");
573 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
575 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
577 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
579 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
581 items.push_back (SeparatorElem());
584 detach_menu (*_size_menu);
587 items.push_back (MenuElem (_("Height"), *_size_menu));
589 items.push_back (SeparatorElem());
591 if (!Profile->get_sae()) {
592 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
593 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
594 items.push_back (SeparatorElem());
597 // Hook for derived classes to add type specific stuff
598 append_extra_display_menu_items ();
602 Menu* layers_menu = manage (new Menu);
603 MenuList &layers_items = layers_menu->items();
604 layers_menu->set_name("ArdourContextMenu");
606 RadioMenuItem::Group layers_group;
608 /* Find out how many overlaid/stacked tracks we have in the selection */
612 TrackSelection const & s = _editor.get_selection().tracks;
613 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
614 StreamView* v = (*i)->view ();
619 switch (v->layer_display ()) {
630 /* We're not connecting to signal_toggled() here; in the case where these two items are
631 set to be in the `inconsistent' state, it seems that one or other will end up active
632 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
633 select the active one, no toggled signal is emitted so nothing happens.
636 _ignore_set_layer_display = true;
638 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
639 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
640 i->set_active (overlaid != 0 && stacked == 0);
641 i->set_inconsistent (overlaid != 0 && stacked != 0);
642 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
644 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
645 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
646 i->set_active (overlaid == 0 && stacked != 0);
647 i->set_inconsistent (overlaid != 0 && stacked != 0);
648 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
650 _ignore_set_layer_display = false;
652 items.push_back (MenuElem (_("Layers"), *layers_menu));
654 if (!Profile->get_sae()) {
656 Menu* alignment_menu = manage (new Menu);
657 MenuList& alignment_items = alignment_menu->items();
658 alignment_menu->set_name ("ArdourContextMenu");
660 RadioMenuItem::Group align_group;
662 /* Same verbose hacks as for the layering options above */
668 boost::shared_ptr<Track> first_track;
670 TrackSelection const & s = _editor.get_selection().tracks;
671 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
672 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
673 if (!r || !r->is_track ()) {
678 first_track = r->track();
681 switch (r->track()->alignment_choice()) {
685 switch (r->track()->alignment_style()) {
686 case ExistingMaterial:
694 case UseExistingMaterial:
710 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 i = s.begin(); i != s.end(); ++i) {
768 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
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));
808 items.push_back (SeparatorElem());
810 build_playlist_menu ();
811 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
812 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
815 route_group_menu->detach ();
818 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
819 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
821 r.push_back (rtv->route ());
826 r.push_back (route ());
829 route_group_menu->build (r);
830 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
832 build_automation_action_menu (true);
833 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
835 items.push_back (SeparatorElem());
839 TrackSelection const & s = _editor.get_selection().tracks;
840 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
841 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
846 if (r->route()->active()) {
853 items.push_back (CheckMenuElem (_("Active")));
854 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
855 bool click_sets_active = true;
856 if (active > 0 && inactive == 0) {
857 i->set_active (true);
858 click_sets_active = false;
859 } else if (active > 0 && inactive > 0) {
860 i->set_inconsistent (true);
862 i->set_sensitive(! _session->transport_rolling());
863 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
865 items.push_back (SeparatorElem());
866 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
867 items.push_front (SeparatorElem());
868 items.push_front (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
872 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
874 if (apply_to_selection) {
875 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
878 bool needs_bounce = false;
880 if (!track()->can_use_mode (mode, needs_bounce)) {
886 cerr << "would bounce this one\n";
891 track()->set_mode (mode);
896 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
898 TimeAxisView::show_timestretch (start, end, layers, layer);
908 /* check that the time selection was made in our route, or our route group.
909 remember that route_group() == 0 implies the route is *not* in a edit group.
912 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
913 /* this doesn't apply to us */
917 /* ignore it if our edit group is not active */
919 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
924 if (timestretch_rect == 0) {
925 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
926 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
927 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
930 timestretch_rect->show ();
931 timestretch_rect->raise_to_top ();
933 double const x1 = start / _editor.get_current_zoom();
934 double const x2 = (end - 1) / _editor.get_current_zoom();
936 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
937 x2, current_height() * (layers - layer) / layers));
941 RouteTimeAxisView::hide_timestretch ()
943 TimeAxisView::hide_timestretch ();
945 if (timestretch_rect) {
946 timestretch_rect->hide ();
951 RouteTimeAxisView::show_selection (TimeSelection& ts)
955 /* ignore it if our edit group is not active or if the selection was started
956 in some other track or route group (remember that route_group() == 0 means
957 that the track is not in an route group).
960 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
961 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
967 TimeAxisView::show_selection (ts);
971 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
974 bool height_changed = (height == 0) || (h != height);
977 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
980 gm.get_level_meter().setup_meters (gmlen, meter_width);
982 TimeAxisView::set_height (h, m);
985 _view->set_height ((double) current_height());
988 if (height >= preset_height (HeightNormal)) {
992 gm.get_gain_slider().show();
994 if (!_route || _route->is_monitor()) {
999 if (rec_enable_button)
1000 rec_enable_button->show();
1002 route_group_button.show();
1003 automation_button.show();
1005 if (is_track() && track()->mode() == ARDOUR::Normal) {
1006 playlist_button.show();
1013 gm.get_gain_slider().hide();
1014 mute_button->show();
1015 if (!_route || _route->is_monitor()) {
1016 solo_button->hide();
1018 solo_button->show();
1020 if (rec_enable_button)
1021 rec_enable_button->show();
1023 route_group_button.hide ();
1024 automation_button.hide ();
1026 if (is_track() && track()->mode() == ARDOUR::Normal) {
1027 playlist_button.hide ();
1032 if (height_changed && !no_redraw) {
1033 /* only emit the signal if the height really changed */
1039 RouteTimeAxisView::route_color_changed ()
1042 _view->apply_color (color(), StreamView::RegionColor);
1045 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1049 RouteTimeAxisView::reset_samples_per_pixel ()
1051 set_samples_per_pixel (_editor.get_current_zoom());
1055 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1060 speed = track()->speed();
1064 _view->set_samples_per_pixel (fpp * speed);
1067 TimeAxisView::set_samples_per_pixel (fpp * speed);
1071 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1073 if (!mitem->get_active()) {
1074 /* this is one of the two calls made when these radio menu items change status. this one
1075 is for the item that became inactive, and we want to ignore it.
1080 if (apply_to_selection) {
1081 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1084 track()->set_align_choice (choice);
1090 RouteTimeAxisView::rename_current_playlist ()
1092 ArdourPrompter prompter (true);
1095 boost::shared_ptr<Track> tr = track();
1096 if (!tr || tr->destructive()) {
1100 boost::shared_ptr<Playlist> pl = tr->playlist();
1105 prompter.set_title (_("Rename Playlist"));
1106 prompter.set_prompt (_("New name for playlist:"));
1107 prompter.set_initial_text (pl->name());
1108 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1109 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1111 switch (prompter.run ()) {
1112 case Gtk::RESPONSE_ACCEPT:
1113 prompter.get_result (name);
1114 if (name.length()) {
1115 pl->set_name (name);
1125 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1127 std::string ret (basename);
1129 std::string const group_string = "." + route_group()->name() + ".";
1131 // iterate through all playlists
1133 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1134 std::string tmp = (*i)->name();
1136 std::string::size_type idx = tmp.find(group_string);
1137 // find those which belong to this group
1138 if (idx != string::npos) {
1139 tmp = tmp.substr(idx + group_string.length());
1141 // and find the largest current number
1143 if (x > maxnumber) {
1152 snprintf (buf, sizeof(buf), "%d", maxnumber);
1154 ret = this->name() + "." + route_group()->name () + "." + buf;
1160 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1164 boost::shared_ptr<Track> tr = track ();
1165 if (!tr || tr->destructive()) {
1169 boost::shared_ptr<const Playlist> pl = tr->playlist();
1176 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1177 name = resolve_new_group_playlist_name(name, playlists_before_op);
1180 while (_session->playlists->by_name(name)) {
1181 name = Playlist::bump_name (name, *_session);
1184 // TODO: The prompter "new" button should be de-activated if the user
1185 // specifies a playlist name which already exists in the session.
1189 ArdourPrompter prompter (true);
1191 prompter.set_title (_("New Copy Playlist"));
1192 prompter.set_prompt (_("Name for new playlist:"));
1193 prompter.set_initial_text (name);
1194 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1195 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1196 prompter.show_all ();
1198 switch (prompter.run ()) {
1199 case Gtk::RESPONSE_ACCEPT:
1200 prompter.get_result (name);
1208 if (name.length()) {
1209 tr->use_copy_playlist ();
1210 tr->playlist()->set_name (name);
1215 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1219 boost::shared_ptr<Track> tr = track ();
1220 if (!tr || tr->destructive()) {
1224 boost::shared_ptr<const Playlist> pl = tr->playlist();
1231 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1232 name = resolve_new_group_playlist_name(name,playlists_before_op);
1235 while (_session->playlists->by_name(name)) {
1236 name = Playlist::bump_name (name, *_session);
1242 ArdourPrompter prompter (true);
1244 prompter.set_title (_("New Playlist"));
1245 prompter.set_prompt (_("Name for new playlist:"));
1246 prompter.set_initial_text (name);
1247 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1248 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1250 switch (prompter.run ()) {
1251 case Gtk::RESPONSE_ACCEPT:
1252 prompter.get_result (name);
1260 if (name.length()) {
1261 tr->use_new_playlist ();
1262 tr->playlist()->set_name (name);
1267 RouteTimeAxisView::clear_playlist ()
1269 boost::shared_ptr<Track> tr = track ();
1270 if (!tr || tr->destructive()) {
1274 boost::shared_ptr<Playlist> pl = tr->playlist();
1279 _editor.clear_playlist (pl);
1283 RouteTimeAxisView::speed_changed ()
1285 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1289 RouteTimeAxisView::update_diskstream_display ()
1299 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1301 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1303 /* special case: select/deselect all tracks */
1305 _editor.begin_reversible_selection_op (X_("Selection Click"));
1307 if (_editor.get_selection().selected (this)) {
1308 _editor.get_selection().clear_tracks ();
1310 _editor.select_all_tracks ();
1313 _editor.commit_reversible_selection_op ();
1318 _editor.begin_reversible_selection_op (X_("Selection Click"));
1320 switch (ArdourKeyboard::selection_type (ev->state)) {
1321 case Selection::Toggle:
1322 _editor.get_selection().toggle (this);
1325 case Selection::Set:
1326 _editor.get_selection().set (this);
1329 case Selection::Extend:
1330 _editor.extend_selection_to_track (*this);
1333 case Selection::Add:
1334 _editor.get_selection().add (this);
1338 _editor.commit_reversible_selection_op ();
1342 RouteTimeAxisView::set_selected_points (PointSelection& points)
1344 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1345 (*i)->set_selected_points (points);
1347 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1349 asv->set_selected_points (points);
1354 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1357 _view->set_selected_regionviews (regions);
1361 /** Add the selectable things that we have to a list.
1362 * @param results List to add things to.
1365 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1370 speed = track()->speed();
1373 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1374 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1376 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1377 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1380 /* pick up visible automation tracks */
1382 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1383 if (!(*i)->hidden()) {
1384 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1390 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1393 _view->get_inverted_selectables (sel, results);
1396 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1397 if (!(*i)->hidden()) {
1398 (*i)->get_inverted_selectables (sel, results);
1406 RouteTimeAxisView::route_group () const
1408 return _route->route_group();
1412 RouteTimeAxisView::name() const
1414 return _route->name();
1417 boost::shared_ptr<Playlist>
1418 RouteTimeAxisView::playlist () const
1420 boost::shared_ptr<Track> tr;
1422 if ((tr = track()) != 0) {
1423 return tr->playlist();
1425 return boost::shared_ptr<Playlist> ();
1430 RouteTimeAxisView::name_entry_changed ()
1432 TimeAxisView::name_entry_changed ();
1434 string x = name_entry->get_text ();
1436 if (x == _route->name()) {
1440 strip_whitespace_edges (x);
1442 if (x.length() == 0) {
1443 name_entry->set_text (_route->name());
1447 if (_session->route_name_internal (x)) {
1448 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1450 name_entry->grab_focus ();
1451 } else if (RouteUI::verify_new_route_name (x)) {
1452 _route->set_name (x);
1454 name_entry->grab_focus ();
1458 boost::shared_ptr<Region>
1459 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1461 boost::shared_ptr<Playlist> pl = playlist ();
1464 return pl->find_next_region (pos, point, dir);
1467 return boost::shared_ptr<Region> ();
1471 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1473 boost::shared_ptr<Playlist> pl = playlist ();
1476 return pl->find_next_region_boundary (pos, dir);
1483 RouteTimeAxisView::fade_range (TimeSelection& selection)
1485 boost::shared_ptr<Playlist> what_we_got;
1486 boost::shared_ptr<Track> tr = track ();
1487 boost::shared_ptr<Playlist> playlist;
1490 /* route is a bus, not a track */
1494 playlist = tr->playlist();
1496 TimeSelection time (selection);
1497 float const speed = tr->speed();
1498 if (speed != 1.0f) {
1499 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1500 (*i).start = session_frame_to_track_frame((*i).start, speed);
1501 (*i).end = session_frame_to_track_frame((*i).end, speed);
1505 playlist->clear_changes ();
1506 playlist->clear_owned_changes ();
1508 playlist->fade_range (time);
1510 vector<Command*> cmds;
1511 playlist->rdiff (cmds);
1512 _session->add_commands (cmds);
1513 _session->add_command (new StatefulDiffCommand (playlist));
1518 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1520 boost::shared_ptr<Playlist> what_we_got;
1521 boost::shared_ptr<Track> tr = track ();
1522 boost::shared_ptr<Playlist> playlist;
1525 /* route is a bus, not a track */
1529 playlist = tr->playlist();
1531 TimeSelection time (selection.time);
1532 float const speed = tr->speed();
1533 if (speed != 1.0f) {
1534 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1535 (*i).start = session_frame_to_track_frame((*i).start, speed);
1536 (*i).end = session_frame_to_track_frame((*i).end, speed);
1540 playlist->clear_changes ();
1541 playlist->clear_owned_changes ();
1545 if (playlist->cut (time) != 0) {
1546 if (Config->get_edit_mode() == Ripple)
1547 playlist->ripple(time.start(), -time.length(), NULL);
1548 // no need to exclude any regions from rippling here
1550 vector<Command*> cmds;
1551 playlist->rdiff (cmds);
1552 _session->add_commands (cmds);
1554 _session->add_command (new StatefulDiffCommand (playlist));
1559 if ((what_we_got = playlist->cut (time)) != 0) {
1560 _editor.get_cut_buffer().add (what_we_got);
1561 if (Config->get_edit_mode() == Ripple)
1562 playlist->ripple(time.start(), -time.length(), NULL);
1563 // no need to exclude any regions from rippling here
1565 vector<Command*> cmds;
1566 playlist->rdiff (cmds);
1567 _session->add_commands (cmds);
1569 _session->add_command (new StatefulDiffCommand (playlist));
1573 if ((what_we_got = playlist->copy (time)) != 0) {
1574 _editor.get_cut_buffer().add (what_we_got);
1579 if ((what_we_got = playlist->cut (time)) != 0) {
1580 if (Config->get_edit_mode() == Ripple)
1581 playlist->ripple(time.start(), -time.length(), NULL);
1582 // no need to exclude any regions from rippling here
1584 vector<Command*> cmds;
1585 playlist->rdiff (cmds);
1586 _session->add_commands (cmds);
1587 _session->add_command (new StatefulDiffCommand (playlist));
1588 what_we_got->release ();
1595 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1601 boost::shared_ptr<Playlist> pl = playlist ();
1602 const ARDOUR::DataType type = pl->data_type();
1603 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1605 if (p == selection.playlists.end()) {
1608 ctx.counts.increase_n_playlists(type);
1610 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1612 if (track()->speed() != 1.0f) {
1613 pos = session_frame_to_track_frame (pos, track()->speed());
1614 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1617 /* add multi-paste offset if applicable */
1618 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1619 const framecnt_t duration = extent.second - extent.first;
1620 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1622 pl->clear_changes ();
1623 pl->clear_owned_changes ();
1624 if (Config->get_edit_mode() == Ripple) {
1625 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1626 framecnt_t amount = extent.second - extent.first;
1627 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1629 pl->paste (*p, pos, ctx.times);
1631 vector<Command*> cmds;
1633 _session->add_commands (cmds);
1635 _session->add_command (new StatefulDiffCommand (pl));
1641 struct PlaylistSorter {
1642 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1643 return a->sort_id() < b->sort_id();
1648 RouteTimeAxisView::build_playlist_menu ()
1650 using namespace Menu_Helpers;
1656 delete playlist_action_menu;
1657 playlist_action_menu = new Menu;
1658 playlist_action_menu->set_name ("ArdourContextMenu");
1660 MenuList& playlist_items = playlist_action_menu->items();
1661 playlist_action_menu->set_name ("ArdourContextMenu");
1662 playlist_items.clear();
1664 RadioMenuItem::Group playlist_group;
1665 boost::shared_ptr<Track> tr = track ();
1667 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1669 /* sort the playlists */
1671 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1673 /* add the playlists to the menu */
1674 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1675 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1676 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1677 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1679 if (tr->playlist()->id() == (*i)->id()) {
1685 playlist_items.push_back (SeparatorElem());
1686 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1687 playlist_items.push_back (SeparatorElem());
1689 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1690 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1691 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1694 // Use a label which tells the user what is happening
1695 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1696 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1700 playlist_items.push_back (SeparatorElem());
1701 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1702 playlist_items.push_back (SeparatorElem());
1704 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1708 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1710 assert (is_track());
1712 // exit if we were triggered by deactivating the old playlist
1713 if (!item->get_active()) {
1717 boost::shared_ptr<Playlist> pl (wpl.lock());
1723 if (track()->playlist() == pl) {
1724 // exit when use_playlist is called by the creation of the playlist menu
1725 // or the playlist choice is unchanged
1729 track()->use_playlist (pl);
1731 RouteGroup* rg = route_group();
1733 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1734 std::string group_string = "." + rg->name() + ".";
1736 std::string take_name = pl->name();
1737 std::string::size_type idx = take_name.find(group_string);
1739 if (idx == std::string::npos)
1742 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1744 boost::shared_ptr<RouteList> rl (rg->route_list());
1746 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1747 if ((*i) == this->route()) {
1751 std::string playlist_name = (*i)->name()+group_string+take_name;
1753 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1758 if (track->freeze_state() == Track::Frozen) {
1759 /* Don't change playlists of frozen tracks */
1763 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1765 // No playlist for this track for this take yet, make it
1766 track->use_new_playlist();
1767 track->playlist()->set_name(playlist_name);
1769 track->use_playlist(ipl);
1776 RouteTimeAxisView::update_playlist_tip ()
1778 RouteGroup* rg = route_group ();
1779 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1780 string group_string = "." + rg->name() + ".";
1782 string take_name = track()->playlist()->name();
1783 string::size_type idx = take_name.find(group_string);
1785 if (idx != string::npos) {
1786 /* find the bit containing the take number / name */
1787 take_name = take_name.substr (idx + group_string.length());
1789 /* set the playlist button tooltip to the take name */
1792 string_compose(_("Take: %1.%2"),
1793 Gtkmm2ext::markup_escape_text (rg->name()),
1794 Gtkmm2ext::markup_escape_text (take_name))
1801 /* set the playlist button tooltip to the playlist name */
1802 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1807 RouteTimeAxisView::show_playlist_selector ()
1809 _editor.playlist_selector().show_for (this);
1813 RouteTimeAxisView::map_frozen ()
1819 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1821 switch (track()->freeze_state()) {
1823 playlist_button.set_sensitive (false);
1824 rec_enable_button->set_sensitive (false);
1827 playlist_button.set_sensitive (true);
1828 rec_enable_button->set_sensitive (true);
1834 RouteTimeAxisView::color_handler ()
1836 //case cTimeStretchOutline:
1837 if (timestretch_rect) {
1838 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1840 //case cTimeStretchFill:
1841 if (timestretch_rect) {
1842 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1848 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1849 * Will add track if necessary.
1852 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1854 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1855 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1858 /* it doesn't exist yet, so we don't care about the button state: just add it */
1859 create_automation_child (param, true);
1862 bool yn = menu->get_active();
1863 bool changed = false;
1865 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1867 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1868 will have done that for us.
1871 if (changed && !no_redraw) {
1879 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1881 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1887 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1889 if (menu && !_hidden) {
1890 ignore_toggle = true;
1891 menu->set_active (false);
1892 ignore_toggle = false;
1895 if (_route && !no_redraw) {
1901 RouteTimeAxisView::update_gain_track_visibility ()
1903 bool const showit = gain_automation_item->get_active();
1905 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1906 gain_track->set_marked_for_display (showit);
1908 /* now trigger a redisplay */
1911 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1917 RouteTimeAxisView::update_trim_track_visibility ()
1919 bool const showit = trim_automation_item->get_active();
1921 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1922 trim_track->set_marked_for_display (showit);
1924 /* now trigger a redisplay */
1927 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1933 RouteTimeAxisView::update_mute_track_visibility ()
1935 bool const showit = mute_automation_item->get_active();
1937 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1938 mute_track->set_marked_for_display (showit);
1940 /* now trigger a redisplay */
1943 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1949 RouteTimeAxisView::update_pan_track_visibility ()
1951 bool const showit = pan_automation_item->get_active();
1952 bool changed = false;
1954 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1955 if ((*i)->set_marked_for_display (showit)) {
1961 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1966 RouteTimeAxisView::ensure_pan_views (bool show)
1968 bool changed = false;
1969 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1971 (*i)->set_marked_for_display (false);
1974 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1978 if (!_route->panner()) {
1982 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1983 set<Evoral::Parameter>::iterator p;
1985 for (p = params.begin(); p != params.end(); ++p) {
1986 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1988 if (pan_control->parameter().type() == NullAutomation) {
1989 error << "Pan control has NULL automation type!" << endmsg;
1993 if (automation_child (pan_control->parameter ()).get () == 0) {
1995 /* we don't already have an AutomationTimeAxisView for this parameter */
1997 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1999 boost::shared_ptr<AutomationTimeAxisView> t (
2000 new AutomationTimeAxisView (_session,
2004 pan_control->parameter (),
2012 pan_tracks.push_back (t);
2013 add_automation_child (*p, t, show);
2015 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2022 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2024 if (apply_to_selection) {
2025 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2029 /* Show our automation */
2031 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2032 i->second->set_marked_for_display (true);
2034 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2037 menu->set_active(true);
2042 /* Show processor automation */
2044 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2045 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2046 if ((*ii)->view == 0) {
2047 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2050 (*ii)->menu_item->set_active (true);
2063 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2065 if (apply_to_selection) {
2066 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2070 /* Show our automation */
2072 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2073 if (i->second->has_automation()) {
2074 i->second->set_marked_for_display (true);
2076 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2078 menu->set_active(true);
2083 /* Show processor automation */
2085 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2086 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2087 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2088 (*ii)->menu_item->set_active (true);
2100 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2102 if (apply_to_selection) {
2103 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2107 /* Hide our automation */
2109 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2110 i->second->set_marked_for_display (false);
2112 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2115 menu->set_active (false);
2119 /* Hide processor automation */
2121 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2122 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2123 (*ii)->menu_item->set_active (false);
2134 RouteTimeAxisView::region_view_added (RegionView* rv)
2136 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2137 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2138 boost::shared_ptr<AutomationTimeAxisView> atv;
2140 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2145 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2146 (*i)->add_ghost(rv);
2150 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2152 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2158 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2160 parent.remove_processor_automation_node (this);
2164 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2167 remove_child (pan->view);
2171 RouteTimeAxisView::ProcessorAutomationNode*
2172 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2174 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2176 if ((*i)->processor == processor) {
2178 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2179 if ((*ii)->what == what) {
2189 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2191 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2194 ProcessorAutomationNode* pan;
2196 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2197 /* session state may never have been saved with new plugin */
2198 error << _("programming error: ")
2199 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2200 processor->name(), what.type(), (int) what.channel(), what.id() )
2202 abort(); /*NOTREACHED*/
2210 boost::shared_ptr<AutomationControl> control
2211 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2213 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2214 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2215 _editor, *this, false, parent_canvas,
2216 processor->describe_parameter (what), processor->name()));
2218 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2220 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2223 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2228 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2231 pan->menu_item->set_active (false);
2240 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2242 boost::shared_ptr<Processor> processor (p.lock ());
2244 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2245 /* The Amp processor is a special case and is dealt with separately */
2249 set<Evoral::Parameter> existing;
2251 processor->what_has_data (existing);
2253 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2255 Evoral::Parameter param (*i);
2256 boost::shared_ptr<AutomationLine> al;
2258 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2261 add_processor_automation_curve (processor, param);
2267 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2269 using namespace Menu_Helpers;
2273 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2275 _automation_tracks[param] = track;
2277 /* existing state overrides "show" argument */
2278 string s = track->gui_property ("visible");
2280 show = string_is_affirmative (s);
2283 /* this might or might not change the visibility status, so don't rely on it */
2284 track->set_marked_for_display (show);
2286 if (show && !no_redraw) {
2290 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2291 /* MIDI-related parameters are always in the menu, there's no
2292 reason to rebuild the menu just because we added a automation
2293 lane for one of them. But if we add a non-MIDI automation
2294 lane, then we need to invalidate the display menu.
2296 delete display_menu;
2302 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2304 boost::shared_ptr<Processor> processor (p.lock ());
2306 if (!processor || !processor->display_to_user ()) {
2310 /* we use this override to veto the Amp processor from the plugin menu,
2311 as its automation lane can be accessed using the special "Fader" menu
2315 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2319 using namespace Menu_Helpers;
2320 ProcessorAutomationInfo *rai;
2321 list<ProcessorAutomationInfo*>::iterator x;
2323 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2325 if (automatable.empty()) {
2329 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2330 if ((*x)->processor == processor) {
2335 if (x == processor_automation.end()) {
2336 rai = new ProcessorAutomationInfo (processor);
2337 processor_automation.push_back (rai);
2342 /* any older menu was deleted at the top of processors_changed()
2343 when we cleared the subplugin menu.
2346 rai->menu = manage (new Menu);
2347 MenuList& items = rai->menu->items();
2348 rai->menu->set_name ("ArdourContextMenu");
2352 std::set<Evoral::Parameter> has_visible_automation;
2353 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2355 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2357 ProcessorAutomationNode* pan;
2358 Gtk::CheckMenuItem* mitem;
2360 string name = processor->describe_parameter (*i);
2362 if (name == X_("hidden")) {
2366 items.push_back (CheckMenuElem (name));
2367 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2369 _subplugin_menu_map[*i] = mitem;
2371 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2372 mitem->set_active(true);
2375 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2379 pan = new ProcessorAutomationNode (*i, mitem, *this);
2381 rai->lines.push_back (pan);
2385 pan->menu_item = mitem;
2389 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2392 if (items.size() == 0) {
2396 /* add the menu for this processor, because the subplugin
2397 menu is always cleared at the top of processors_changed().
2398 this is the result of some poor design in gtkmm and/or
2402 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2407 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2408 RouteTimeAxisView::ProcessorAutomationNode* pan)
2410 bool showit = pan->menu_item->get_active();
2411 bool redraw = false;
2413 if (pan->view == 0 && showit) {
2414 add_processor_automation_curve (rai->processor, pan->what);
2418 if (pan->view && pan->view->set_marked_for_display (showit)) {
2422 if (redraw && !no_redraw) {
2428 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2430 if (c.type == RouteProcessorChange::MeterPointChange) {
2431 /* nothing to do if only the meter point has changed */
2435 using namespace Menu_Helpers;
2437 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2438 (*i)->valid = false;
2441 setup_processor_menu_and_curves ();
2443 bool deleted_processor_automation = false;
2445 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2447 list<ProcessorAutomationInfo*>::iterator tmp;
2455 processor_automation.erase (i);
2456 deleted_processor_automation = true;
2463 if (deleted_processor_automation && !no_redraw) {
2468 boost::shared_ptr<AutomationLine>
2469 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2471 ProcessorAutomationNode* pan;
2473 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2479 return boost::shared_ptr<AutomationLine>();
2483 RouteTimeAxisView::reset_processor_automation_curves ()
2485 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2491 RouteTimeAxisView::can_edit_name () const
2493 /* we do not allow track name changes if it is record enabled
2495 return !_route->record_enabled();
2499 RouteTimeAxisView::blink_rec_display (bool onoff)
2501 RouteUI::blink_rec_display (onoff);
2505 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2507 if (_ignore_set_layer_display) {
2511 if (apply_to_selection) {
2512 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2516 _view->set_layer_display (d);
2519 set_gui_property (X_("layer-display"), enum_2_string (d));
2524 RouteTimeAxisView::layer_display () const
2527 return _view->layer_display ();
2530 /* we don't know, since we don't have a _view, so just return something */
2536 boost::shared_ptr<AutomationTimeAxisView>
2537 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2539 AutomationTracks::iterator i = _automation_tracks.find(param);
2540 if (i != _automation_tracks.end()) {
2543 return boost::shared_ptr<AutomationTimeAxisView>();
2548 RouteTimeAxisView::fast_update ()
2550 gm.get_level_meter().update_meters ();
2554 RouteTimeAxisView::hide_meter ()
2557 gm.get_level_meter().hide_meters ();
2561 RouteTimeAxisView::show_meter ()
2567 RouteTimeAxisView::reset_meter ()
2569 if (UIConfiguration::instance().get_show_track_meters()) {
2570 int meter_width = 3;
2571 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2574 gm.get_level_meter().setup_meters (height - 9, meter_width);
2581 RouteTimeAxisView::clear_meter ()
2583 gm.get_level_meter().clear_meters ();
2587 RouteTimeAxisView::meter_changed ()
2589 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2591 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2594 // reset peak when meter point changes
2595 gm.reset_peak_display();
2599 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2602 if (_route && !no_redraw) {
2608 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2610 using namespace Menu_Helpers;
2612 if (!_underlay_streams.empty()) {
2613 MenuList& parent_items = parent_menu->items();
2614 Menu* gs_menu = manage (new Menu);
2615 gs_menu->set_name ("ArdourContextMenu");
2616 MenuList& gs_items = gs_menu->items();
2618 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2620 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2621 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2622 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2628 RouteTimeAxisView::set_underlay_state()
2630 if (!underlay_xml_node) {
2634 XMLNodeList nlist = underlay_xml_node->children();
2635 XMLNodeConstIterator niter;
2636 XMLNode *child_node;
2638 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2639 child_node = *niter;
2641 if (child_node->name() != "Underlay") {
2645 XMLProperty* prop = child_node->property ("id");
2647 PBD::ID id (prop->value());
2649 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2652 add_underlay(v->view(), false);
2661 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2667 RouteTimeAxisView& other = v->trackview();
2669 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2670 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2671 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2672 abort(); /*NOTREACHED*/
2675 _underlay_streams.push_back(v);
2676 other._underlay_mirrors.push_back(this);
2678 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2680 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2682 if (!underlay_xml_node) {
2683 underlay_xml_node = xml_node->add_child("Underlays");
2686 XMLNode* node = underlay_xml_node->add_child("Underlay");
2687 XMLProperty* prop = node->add_property("id");
2688 prop->set_value(v->trackview().route()->id().to_s());
2695 RouteTimeAxisView::remove_underlay (StreamView* v)
2701 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2702 RouteTimeAxisView& other = v->trackview();
2704 if (it != _underlay_streams.end()) {
2705 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2707 if (gm == other._underlay_mirrors.end()) {
2708 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2709 abort(); /*NOTREACHED*/
2712 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2714 _underlay_streams.erase(it);
2715 other._underlay_mirrors.erase(gm);
2717 if (underlay_xml_node) {
2718 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2724 RouteTimeAxisView::set_button_names ()
2726 if (_route && _route->solo_safe()) {
2727 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2729 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2731 if (Config->get_solo_control_is_listen_control()) {
2732 switch (Config->get_listen_position()) {
2733 case AfterFaderListen:
2734 solo_button->set_text (S_("AfterFader|A"));
2735 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2737 case PreFaderListen:
2738 solo_button->set_text (S_("PreFader|P"));
2739 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2743 solo_button->set_text (S_("Solo|S"));
2744 set_tooltip (*solo_button, _("Solo"));
2746 mute_button->set_text (S_("Mute|M"));
2750 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2752 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2753 if (i != _main_automation_menu_map.end()) {
2757 i = _subplugin_menu_map.find (param);
2758 if (i != _subplugin_menu_map.end()) {
2766 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2768 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2770 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2774 gain_track.reset (new AutomationTimeAxisView (_session,
2775 _route, _route->amp(), c, param,
2780 _route->amp()->describe_parameter(param)));
2783 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2786 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2790 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2792 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2793 if (!c || ! _route->trim()->active()) {
2797 trim_track.reset (new AutomationTimeAxisView (_session,
2798 _route, _route->trim(), c, param,
2803 _route->trim()->describe_parameter(param)));
2806 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2809 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2813 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2815 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2817 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2821 mute_track.reset (new AutomationTimeAxisView (_session,
2822 _route, _route, c, param,
2827 _route->describe_parameter(param)));
2830 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2833 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2837 void add_region_to_list (RegionView* rv, RegionList* l)
2839 l->push_back (rv->region());
2843 RouteTimeAxisView::combine_regions ()
2845 /* as of may 2011, we do not offer uncombine for MIDI tracks
2848 if (!is_audio_track()) {
2856 RegionList selected_regions;
2857 boost::shared_ptr<Playlist> playlist = track()->playlist();
2859 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2861 if (selected_regions.size() < 2) {
2865 playlist->clear_changes ();
2866 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2868 _session->add_command (new StatefulDiffCommand (playlist));
2869 /* make the new region be selected */
2871 return _view->find_view (compound_region);
2875 RouteTimeAxisView::uncombine_regions ()
2877 /* as of may 2011, we do not offer uncombine for MIDI tracks
2879 if (!is_audio_track()) {
2887 RegionList selected_regions;
2888 boost::shared_ptr<Playlist> playlist = track()->playlist();
2890 /* have to grab selected regions first because the uncombine is going
2891 * to change that in the middle of the list traverse
2894 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2896 playlist->clear_changes ();
2898 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2899 playlist->uncombine (*i);
2902 _session->add_command (new StatefulDiffCommand (playlist));
2906 RouteTimeAxisView::state_id() const
2908 return string_compose ("rtav %1", _route->id().to_s());
2913 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2915 TimeAxisView::remove_child (c);
2917 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2919 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2920 if (i->second == a) {
2921 _automation_tracks.erase (i);