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 <gtkmm/menu.h>
32 #include <gtkmm/menuitem.h>
33 #include <gtkmm/stock.h>
35 #include "pbd/error.h"
36 #include "pbd/stl_delete.h"
37 #include "pbd/whitespace.h"
38 #include "pbd/memento_command.h"
39 #include "pbd/enumwriter.h"
40 #include "pbd/stateful_diff_command.h"
42 #include "evoral/Parameter.hpp"
44 #include "ardour/amp.h"
45 #include "ardour/meter.h"
46 #include "ardour/event_type_map.h"
47 #include "ardour/pannable.h"
48 #include "ardour/panner.h"
49 #include "ardour/plugin_insert.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"
55 #include "ardour/track.h"
57 #include "canvas/debug.h"
59 #include "gtkmm2ext/gtk_ui.h"
60 #include "gtkmm2ext/utils.h"
62 #include "widgets/ardour_button.h"
63 #include "widgets/prompter.h"
64 #include "widgets/tooltips.h"
66 #include "ardour_ui.h"
67 #include "audio_streamview.h"
69 #include "enums_convert.h"
70 #include "route_time_axis.h"
71 #include "automation_time_axis.h"
73 #include "gui_thread.h"
74 #include "item_counts.h"
76 #include "paste_context.h"
77 #include "patch_change_widget.h"
78 #include "playlist_selector.h"
79 #include "point_selection.h"
80 #include "public_editor.h"
81 #include "region_view.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "streamview.h"
85 #include "ui_config.h"
87 #include "route_group_menu.h"
91 using namespace ARDOUR;
92 using namespace ArdourWidgets;
94 using namespace Gtkmm2ext;
96 using namespace Editing;
100 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
102 , StripableTimeAxisView(ed, sess, 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 , pan_automation_item(NULL)
118 number_label.set_name("tracknumber label");
119 number_label.set_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
120 number_label.set_alignment(.5, .5);
121 number_label.set_fallthrough_to_parent (true);
123 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
124 UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::parameter_changed));
126 parameter_changed ("editor-stereo-only-meters");
130 RouteTimeAxisView::route_property_changed (const PBD::PropertyChange& what_changed)
132 if (what_changed.contains (ARDOUR::Properties::name)) {
138 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
140 RouteUI::set_route (rt);
141 StripableTimeAxisView::set_stripable (rt);
143 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
144 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
145 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
148 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
151 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp(), _route->gain_control());
152 gm.get_level_meter().set_no_show_all();
153 gm.get_level_meter().setup_meters(50, meter_width);
154 gm.update_gain_sensitive ();
157 if (get_gui_property ("height", height)) {
160 set_height (preset_height (HeightNormal));
163 if (!_route->is_auditioner()) {
164 if (gui_property ("visible").empty()) {
165 set_gui_property ("visible", true);
168 set_gui_property ("visible", false);
171 timestretch_rect = 0;
174 ignore_toggle = false;
176 route_group_button.set_name ("route button");
177 playlist_button.set_name ("route button");
178 automation_button.set_name ("route button");
180 route_group_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
181 playlist_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click), false);
182 automation_button.signal_button_press_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click), false);
186 if (ARDOUR::Profile->get_mixbus()) {
187 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
189 controls_table.attach (*rec_enable_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
192 if (is_midi_track()) {
193 set_tooltip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
194 gm.set_fader_name ("MidiTrackFader");
196 set_tooltip(*rec_enable_button, _("Record"));
197 gm.set_fader_name ("AudioTrackFader");
200 /* set playlist button tip to the current playlist, and make it update when it changes */
201 update_playlist_tip ();
202 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
205 gm.set_fader_name ("AudioBusFader");
206 Gtk::Fixed *blank = manage(new Gtk::Fixed());
207 controls_button_size_group->add_widget(*blank);
208 if (ARDOUR::Profile->get_mixbus() ) {
209 controls_table.attach (*blank, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
211 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
216 top_hbox.pack_end(gm.get_level_meter(), false, false, 2);
218 if (!ARDOUR::Profile->get_mixbus()) {
219 controls_meters_size_group->add_widget (gm.get_level_meter());
222 if (_route->is_master()) {
223 route_group_button.set_sensitive(false);
226 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
227 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
228 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
229 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
231 if (ARDOUR::Profile->get_mixbus()) {
232 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
234 controls_table.attach (*mute_button, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
236 // mute button is always present, it is used to
237 // force the 'blank' placeholders to the proper size
238 controls_button_size_group->add_widget(*mute_button);
240 if (!_route->is_master()) {
241 if (ARDOUR::Profile->get_mixbus()) {
242 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
244 controls_table.attach (*solo_button, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
247 Gtk::Fixed *blank = manage(new Gtk::Fixed());
248 controls_button_size_group->add_widget(*blank);
249 if (ARDOUR::Profile->get_mixbus()) {
250 controls_table.attach (*blank, 2, 3, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
252 controls_table.attach (*blank, 4, 5, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
257 if (ARDOUR::Profile->get_mixbus()) {
258 controls_table.attach (route_group_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
259 controls_table.attach (gm.get_gain_slider(), 3, 5, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
261 else if (!ARDOUR::Profile->get_trx()) {
262 controls_table.attach (route_group_button, 4, 5, 2, 3, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
263 controls_table.attach (gm.get_gain_slider(), 0, 2, 2, 3, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 1, 0);
266 set_tooltip(*solo_button,_("Solo"));
267 set_tooltip(*mute_button,_("Mute"));
268 set_tooltip(route_group_button, _("Route Group"));
270 mute_button->set_tweaks(ArdourButton::TrackHeader);
271 solo_button->set_tweaks(ArdourButton::TrackHeader);
272 rec_enable_button->set_tweaks(ArdourButton::TrackHeader);
273 playlist_button.set_tweaks(ArdourButton::TrackHeader);
274 automation_button.set_tweaks(ArdourButton::TrackHeader);
275 route_group_button.set_tweaks(ArdourButton::TrackHeader);
277 if (is_midi_track()) {
278 set_tooltip(automation_button, _("MIDI Controllers and Automation"));
280 set_tooltip(automation_button, _("Automation"));
283 update_track_number_visibility();
286 if (ARDOUR::Profile->get_mixbus()) {
287 controls_table.attach (automation_button, 1, 2, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
289 else if (!ARDOUR::Profile->get_trx()) {
290 controls_table.attach (automation_button, 3, 4, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
293 if (is_track() && track()->mode() == ARDOUR::Normal) {
294 if (ARDOUR::Profile->get_mixbus()) {
295 controls_table.attach (playlist_button, 0, 1, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
297 else if (!ARDOUR::Profile->get_trx()) {
298 controls_table.attach (playlist_button, 2, 3, 2, 3, Gtk::SHRINK, Gtk::SHRINK);
304 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
308 LayerDisplay layer_display;
309 if (get_gui_property ("layer-display", layer_display)) {
310 set_layer_display (layer_display);
313 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
314 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
316 /* pick up the correct freeze state */
321 UIConfiguration::instance().ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
323 PropertyList* plist = new PropertyList();
325 plist->add (ARDOUR::Properties::group_mute, true);
326 plist->add (ARDOUR::Properties::group_solo, true);
328 route_group_menu = new RouteGroupMenu (_session, plist);
330 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
333 RouteTimeAxisView::~RouteTimeAxisView ()
335 cleanup_gui_properties ();
337 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
341 delete playlist_action_menu;
342 playlist_action_menu = 0;
347 _automation_tracks.clear ();
349 delete route_group_menu;
350 CatchDeletion (this);
354 RouteTimeAxisView::name() const
357 return _route->name();
363 RouteTimeAxisView::post_construct ()
365 /* map current state of the route */
367 update_diskstream_display ();
368 setup_processor_menu_and_curves ();
369 reset_processor_automation_curves ();
372 /** Set up the processor menu for the current set of processors, and
373 * display automation curves for any parameters which have data.
376 RouteTimeAxisView::setup_processor_menu_and_curves ()
378 _subplugin_menu_map.clear ();
379 subplugin_menu.items().clear ();
380 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
381 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
385 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
387 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
388 if (_route->route_group()) {
389 _route->route_group()->remove (_route);
395 r.push_back (route ());
397 route_group_menu->build (r);
398 if (ev->button == 1) {
399 Gtkmm2ext::anchored_menu_popup(route_group_menu->menu(),
403 route_group_menu->menu()->popup (ev->button, ev->time);
410 RouteTimeAxisView::playlist_changed ()
416 RouteTimeAxisView::label_view ()
418 string x = _route->name ();
419 if (x != name_label.get_text ()) {
420 name_label.set_text (x);
422 const int64_t track_number = _route->track_number ();
423 if (track_number == 0) {
424 number_label.set_text ("");
426 number_label.set_text (PBD::to_string (abs(_route->track_number ())));
431 RouteTimeAxisView::update_track_number_visibility ()
434 bool show_label = _session->config.get_track_name_number();
436 if (_route && _route->is_master()) {
440 if (number_label.get_parent()) {
441 controls_table.remove (number_label);
444 if (ARDOUR::Profile->get_mixbus()) {
445 controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
447 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND|Gtk::FILL, 1, 0);
449 // see ArdourButton::on_size_request(), we should probably use a global size-group here instead.
450 // except the width of the number label is subtracted from the name-hbox, so we
451 // need to explictly calculate it anyway until the name-label & entry become ArdourWidgets.
452 int tnw = (2 + std::max(2u, _session->track_number_decimals())) * number_label.char_pixel_width();
454 number_label.set_size_request(tnw, -1);
455 number_label.show ();
457 number_label.hide ();
462 RouteTimeAxisView::parameter_changed (string const & p)
464 if (p == "track-name-number") {
465 update_track_number_visibility();
466 } else if (p == "editor-stereo-only-meters") {
467 if (UIConfiguration::instance().get_editor_stereo_only_meters()) {
468 gm.get_level_meter().set_max_audio_meter_count (2);
470 gm.get_level_meter().set_max_audio_meter_count (0);
476 RouteTimeAxisView::take_name_changed (void *src)
484 RouteTimeAxisView::playlist_click (GdkEventButton *ev)
486 if (ev->button != 1) {
490 build_playlist_menu ();
491 conditionally_add_to_selection ();
492 Gtkmm2ext::anchored_menu_popup(playlist_action_menu, &playlist_button,
498 RouteTimeAxisView::automation_click (GdkEventButton *ev)
500 if (ev->button != 1) {
504 conditionally_add_to_selection ();
505 build_automation_action_menu (false);
506 Gtkmm2ext::anchored_menu_popup(automation_action_menu, &automation_button,
512 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
514 using namespace Menu_Helpers;
516 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
517 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
520 detach_menu (subplugin_menu);
522 _main_automation_menu_map.clear ();
523 delete automation_action_menu;
524 automation_action_menu = new Menu;
526 MenuList& items = automation_action_menu->items();
528 automation_action_menu->set_name ("ArdourContextMenu");
530 items.push_back (MenuElem (_("Show All Automation"),
531 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
533 items.push_back (MenuElem (_("Show Existing Automation"),
534 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
536 items.push_back (MenuElem (_("Hide All Automation"),
537 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
539 /* Attach the plugin submenu. It may have previously been used elsewhere,
540 so it was detached above
543 bool single_track_selected = (!for_selection || _editor.get_selection().tracks.size() == 1);
545 if (!subplugin_menu.items().empty()) {
546 items.push_back (SeparatorElem ());
547 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
548 items.back().set_sensitive (single_track_selected);
551 /* Add any route automation */
554 items.push_back (CheckMenuElem (_("Fader"), sigc::mem_fun (*this, &RouteTimeAxisView::update_gain_track_visibility)));
555 gain_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
556 gain_automation_item->set_active (single_track_selected &&
557 string_to<bool>(gain_track->gui_property ("visible")));
559 _main_automation_menu_map[Evoral::Parameter(GainAutomation)] = gain_automation_item;
563 items.push_back (CheckMenuElem (_("Trim"), sigc::mem_fun (*this, &RouteTimeAxisView::update_trim_track_visibility)));
564 trim_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
565 trim_automation_item->set_active (single_track_selected &&
566 string_to<bool>(trim_track->gui_property ("visible")));
568 _main_automation_menu_map[Evoral::Parameter(TrimAutomation)] = trim_automation_item;
572 items.push_back (CheckMenuElem (_("Mute"), sigc::mem_fun (*this, &RouteTimeAxisView::update_mute_track_visibility)));
573 mute_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
574 mute_automation_item->set_active (single_track_selected &&
575 string_to<bool>(mute_track->gui_property ("visible")));
577 _main_automation_menu_map[Evoral::Parameter(MuteAutomation)] = mute_automation_item;
580 if (!pan_tracks.empty() && !ARDOUR::Profile->get_mixbus()) {
581 items.push_back (CheckMenuElem (_("Pan"), sigc::mem_fun (*this, &RouteTimeAxisView::update_pan_track_visibility)));
582 pan_automation_item = dynamic_cast<Gtk::CheckMenuItem*> (&items.back ());
583 pan_automation_item->set_active (single_track_selected &&
584 string_to<bool>(pan_tracks.front()->gui_property ("visible")));
586 set<Evoral::Parameter> const & params = _route->pannable()->what_can_be_automated ();
587 for (set<Evoral::Parameter>::const_iterator p = params.begin(); p != params.end(); ++p) {
588 _main_automation_menu_map[*p] = pan_automation_item;
594 RouteTimeAxisView::build_display_menu ()
596 using namespace Menu_Helpers;
600 TimeAxisView::build_display_menu ();
602 /* now fill it with our stuff */
604 MenuList& items = display_menu->items();
605 display_menu->set_name ("ArdourContextMenu");
607 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
609 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
611 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
613 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
615 items.push_back (SeparatorElem());
618 detach_menu (*_size_menu);
621 items.push_back (MenuElem (_("Height"), *_size_menu));
622 items.push_back (SeparatorElem());
624 // Hook for derived classes to add type specific stuff
625 append_extra_display_menu_items ();
629 Menu* layers_menu = manage (new Menu);
630 MenuList &layers_items = layers_menu->items();
631 layers_menu->set_name("ArdourContextMenu");
633 RadioMenuItem::Group layers_group;
635 /* Find out how many overlaid/stacked tracks we have in the selection */
639 int unchangeable = 0;
640 TrackSelection const & s = _editor.get_selection().tracks;
642 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
643 StreamView* v = (*i)->view ();
648 if (v->can_change_layer_display()) {
649 switch (v->layer_display ()) {
663 /* We're not connecting to signal_toggled() here; in the case where these two items are
664 set to be in the `inconsistent' state, it seems that one or other will end up active
665 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
666 select the active one, no toggled signal is emitted so nothing happens.
669 _ignore_set_layer_display = true;
671 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
672 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
673 i->set_active (overlaid != 0 && stacked == 0);
674 i->set_inconsistent (overlaid != 0 && stacked != 0);
675 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
678 i->set_sensitive (false);
681 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
682 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
683 i->set_active (overlaid == 0 && stacked != 0);
684 i->set_inconsistent (overlaid != 0 && stacked != 0);
685 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
688 i->set_sensitive (false);
691 _ignore_set_layer_display = false;
693 items.push_back (MenuElem (_("Layers"), *layers_menu));
695 Menu* alignment_menu = manage (new Menu);
696 MenuList& alignment_items = alignment_menu->items();
697 alignment_menu->set_name ("ArdourContextMenu");
699 RadioMenuItem::Group align_group;
701 /* Same verbose hacks as for the layering options above */
707 boost::shared_ptr<Track> first_track;
709 for (TrackSelection::const_iterator t = s.begin(); t != s.end(); ++t) {
710 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*t);
711 if (!r || !r->is_track ()) {
716 first_track = r->track();
719 switch (r->track()->alignment_choice()) {
723 switch (r->track()->alignment_style()) {
724 case ExistingMaterial:
732 case UseExistingMaterial:
748 inconsistent = false;
755 if (!inconsistent && first_track) {
757 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
758 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
759 i->set_active (automatic != 0 && existing == 0 && capture == 0);
760 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
762 switch (first_track->alignment_choice()) {
764 switch (first_track->alignment_style()) {
765 case ExistingMaterial:
766 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
769 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
777 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
778 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
779 i->set_active (existing != 0 && capture == 0 && automatic == 0);
780 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
782 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
783 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
784 i->set_active (existing == 0 && capture != 0 && automatic == 0);
785 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
787 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
793 items.push_back (SeparatorElem());
795 build_playlist_menu ();
796 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
797 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
800 if (!is_midi_track () && _route->the_instrument ()) {
802 items.push_back (MenuElem (_("Patch Selector..."),
803 sigc::mem_fun(*this, &RouteUI::select_midi_patch)));
804 items.push_back (SeparatorElem());
807 route_group_menu->detach ();
810 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
811 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
813 r.push_back (rtv->route ());
818 r.push_back (route ());
821 if (!_route->is_master()) {
822 route_group_menu->build (r);
823 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
826 build_automation_action_menu (true);
827 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
829 items.push_back (SeparatorElem());
833 TrackSelection const & s = _editor.get_selection().tracks;
834 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
835 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
840 if (r->route()->active()) {
847 items.push_back (CheckMenuElem (_("Active")));
848 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
849 bool click_sets_active = true;
850 if (active > 0 && inactive == 0) {
851 i->set_active (true);
852 click_sets_active = false;
853 } else if (active > 0 && inactive > 0) {
854 i->set_inconsistent (true);
856 i->set_sensitive(! _session->transport_rolling());
857 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
859 items.push_back (SeparatorElem());
860 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
861 if (_route && !_route->is_master()) {
862 items.push_back (SeparatorElem());
863 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
865 items.push_back (SeparatorElem());
866 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
870 RouteTimeAxisView::show_timestretch (samplepos_t start, samplepos_t end, int layers, int layer)
872 TimeAxisView::show_timestretch (start, end, layers, layer);
882 /* check that the time selection was made in our route, or our route group.
883 remember that route_group() == 0 implies the route is *not* in a edit group.
886 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
887 /* this doesn't apply to us */
891 /* ignore it if our edit group is not active */
893 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
898 if (timestretch_rect == 0) {
899 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
900 timestretch_rect->set_fill_color (Gtkmm2ext::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
901 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
904 timestretch_rect->show ();
905 timestretch_rect->raise_to_top ();
907 double const x1 = start / _editor.get_current_zoom();
908 double const x2 = (end - 1) / _editor.get_current_zoom();
910 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
911 x2, current_height() * (layers - layer) / layers));
915 RouteTimeAxisView::hide_timestretch ()
917 TimeAxisView::hide_timestretch ();
919 if (timestretch_rect) {
920 timestretch_rect->hide ();
925 RouteTimeAxisView::show_selection (TimeSelection& ts)
929 /* ignore it if our edit group is not active or if the selection was started
930 in some other track or route group (remember that route_group() == 0 means
931 that the track is not in an route group).
934 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
935 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
941 TimeAxisView::show_selection (ts);
945 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
948 bool height_changed = (height == 0) || (h != height);
951 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
954 gm.get_level_meter().setup_meters (gmlen, meter_width);
956 TimeAxisView::set_height (h, m);
959 _view->set_height ((double) current_height());
962 if (height >= preset_height (HeightNormal)) {
966 gm.get_gain_slider().show();
968 if (!_route || _route->is_monitor()) {
973 if (rec_enable_button)
974 rec_enable_button->show();
976 route_group_button.show();
977 automation_button.show();
979 if (is_track() && track()->mode() == ARDOUR::Normal) {
980 playlist_button.show();
987 gm.get_gain_slider().hide();
989 if (!_route || _route->is_monitor()) {
994 if (rec_enable_button)
995 rec_enable_button->show();
997 route_group_button.hide ();
998 automation_button.hide ();
1000 if (is_track() && track()->mode() == ARDOUR::Normal) {
1001 playlist_button.hide ();
1006 if (height_changed && !no_redraw) {
1007 /* only emit the signal if the height really changed */
1013 RouteTimeAxisView::route_color_changed ()
1015 using namespace ARDOUR_UI_UTILS;
1017 _view->apply_color (color(), StreamView::RegionColor);
1019 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1023 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1026 _view->set_samples_per_pixel (fpp);
1029 StripableTimeAxisView::set_samples_per_pixel (fpp);
1033 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1035 if (!mitem->get_active()) {
1036 /* this is one of the two calls made when these radio menu items change status. this one
1037 is for the item that became inactive, and we want to ignore it.
1042 if (apply_to_selection) {
1043 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1046 track()->set_align_choice (choice);
1052 RouteTimeAxisView::rename_current_playlist ()
1054 Prompter prompter (true);
1057 boost::shared_ptr<Track> tr = track();
1058 if (!tr || tr->destructive()) {
1062 boost::shared_ptr<Playlist> pl = tr->playlist();
1067 prompter.set_title (_("Rename Playlist"));
1068 prompter.set_prompt (_("New name for playlist:"));
1069 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1070 prompter.set_initial_text (pl->name());
1071 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1074 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1077 prompter.get_result (name);
1078 if (name.length()) {
1079 if (_session->playlists->by_name (name)) {
1080 MessageDialog msg (_("Given playlist name is not unique."));
1082 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1084 pl->set_name (name);
1092 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1094 std::string ret (basename);
1096 std::string const group_string = "." + route_group()->name() + ".";
1098 // iterate through all playlists
1100 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1101 std::string tmp = (*i)->name();
1103 std::string::size_type idx = tmp.find(group_string);
1104 // find those which belong to this group
1105 if (idx != string::npos) {
1106 tmp = tmp.substr(idx + group_string.length());
1108 // and find the largest current number
1110 if (x > maxnumber) {
1119 snprintf (buf, sizeof(buf), "%d", maxnumber);
1121 ret = this->name() + "." + route_group()->name () + "." + buf;
1127 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op, bool copy)
1131 boost::shared_ptr<Track> tr = track ();
1132 if (!tr || tr->destructive()) {
1136 boost::shared_ptr<const Playlist> pl = tr->playlist();
1143 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1144 name = resolve_new_group_playlist_name(name,playlists_before_op);
1147 while (_session->playlists->by_name(name)) {
1148 name = Playlist::bump_name (name, *_session);
1152 // TODO: The prompter "new" button should be de-activated if the user
1153 // specifies a playlist name which already exists in the session.
1155 Prompter prompter (true);
1158 prompter.set_title (_("New Copy Playlist"));
1159 prompter.set_prompt (_("Name for playlist copy:"));
1161 prompter.set_title (_("New Playlist"));
1162 prompter.set_prompt (_("Name for new playlist:"));
1164 prompter.set_initial_text (name);
1165 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1166 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1167 prompter.show_all ();
1170 if (prompter.run () != Gtk::RESPONSE_ACCEPT) {
1173 prompter.get_result (name);
1174 if (name.length()) {
1175 if (_session->playlists->by_name (name)) {
1176 MessageDialog msg (_("Given playlist name is not unique."));
1178 prompter.set_initial_text (Playlist::bump_name (name, *_session));
1186 if (name.length()) {
1188 tr->use_copy_playlist ();
1190 tr->use_default_new_playlist ();
1192 tr->playlist()->set_name (name);
1197 RouteTimeAxisView::clear_playlist ()
1199 boost::shared_ptr<Track> tr = track ();
1200 if (!tr || tr->destructive()) {
1204 boost::shared_ptr<Playlist> pl = tr->playlist();
1209 _editor.clear_playlist (pl);
1213 RouteTimeAxisView::speed_changed ()
1215 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1219 RouteTimeAxisView::update_diskstream_display ()
1229 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1231 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1233 /* special case: select/deselect all tracks */
1235 _editor.begin_reversible_selection_op (X_("Selection Click"));
1237 if (_editor.get_selection().selected (this)) {
1238 _editor.get_selection().clear_tracks ();
1240 _editor.select_all_tracks ();
1243 _editor.commit_reversible_selection_op ();
1248 _editor.begin_reversible_selection_op (X_("Selection Click"));
1250 switch (ArdourKeyboard::selection_type (ev->state)) {
1251 case Selection::Toggle:
1252 _editor.get_selection().toggle (this);
1255 case Selection::Set:
1256 _editor.get_selection().set (this);
1259 case Selection::Extend:
1260 _editor.extend_selection_to_track (*this);
1263 case Selection::Add:
1264 _editor.get_selection().add (this);
1268 _editor.commit_reversible_selection_op ();
1270 _editor.set_selected_mixer_strip (*this);
1274 RouteTimeAxisView::set_selected_points (PointSelection& points)
1276 StripableTimeAxisView::set_selected_points (points);
1277 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1279 asv->set_selected_points (points);
1284 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1287 _view->set_selected_regionviews (regions);
1291 /** Add the selectable things that we have to a list.
1292 * @param results List to add things to.
1295 RouteTimeAxisView::get_selectables (samplepos_t start, samplepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1297 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1298 _view->get_selectables (start, end, top, bot, results, within);
1301 /* pick up visible automation tracks */
1302 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1303 if (!(*i)->hidden()) {
1304 (*i)->get_selectables (start, end, top, bot, results, within);
1310 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1313 _view->get_inverted_selectables (sel, results);
1315 StripableTimeAxisView::get_inverted_selectables (sel, results);
1319 RouteTimeAxisView::route_group () const
1321 return _route->route_group();
1324 boost::shared_ptr<Playlist>
1325 RouteTimeAxisView::playlist () const
1327 boost::shared_ptr<Track> tr;
1329 if ((tr = track()) != 0) {
1330 return tr->playlist();
1332 return boost::shared_ptr<Playlist> ();
1337 RouteTimeAxisView::name_entry_changed (string const& str)
1339 if (str == _route->name()) {
1345 strip_whitespace_edges (x);
1351 if (_session->route_name_internal (x)) {
1352 ARDOUR_UI::instance()->popup_error (string_compose (_("The name \"%1\" is reserved for %2"), x, PROGRAM_NAME));
1354 } else if (RouteUI::verify_new_route_name (x)) {
1355 _route->set_name (x);
1362 boost::shared_ptr<Region>
1363 RouteTimeAxisView::find_next_region (samplepos_t pos, RegionPoint point, int32_t dir)
1365 boost::shared_ptr<Playlist> pl = playlist ();
1368 return pl->find_next_region (pos, point, dir);
1371 return boost::shared_ptr<Region> ();
1375 RouteTimeAxisView::find_next_region_boundary (samplepos_t pos, int32_t dir)
1377 boost::shared_ptr<Playlist> pl = playlist ();
1380 return pl->find_next_region_boundary (pos, dir);
1387 RouteTimeAxisView::fade_range (TimeSelection& selection)
1389 boost::shared_ptr<Playlist> what_we_got;
1390 boost::shared_ptr<Track> tr = track ();
1391 boost::shared_ptr<Playlist> playlist;
1394 /* route is a bus, not a track */
1398 playlist = tr->playlist();
1400 TimeSelection time (selection);
1402 playlist->clear_changes ();
1403 playlist->clear_owned_changes ();
1405 playlist->fade_range (time);
1407 vector<Command*> cmds;
1408 playlist->rdiff (cmds);
1409 _session->add_commands (cmds);
1410 _session->add_command (new StatefulDiffCommand (playlist));
1415 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1417 boost::shared_ptr<Playlist> what_we_got;
1418 boost::shared_ptr<Track> tr = track ();
1419 boost::shared_ptr<Playlist> playlist;
1422 /* route is a bus, not a track */
1426 playlist = tr->playlist();
1428 TimeSelection time (selection.time);
1430 playlist->clear_changes ();
1431 playlist->clear_owned_changes ();
1435 if (playlist->cut (time) != 0) {
1436 if (Config->get_edit_mode() == Ripple) {
1437 playlist->ripple(time.start(), -time.length(), NULL);
1439 // no need to exclude any regions from rippling here
1441 vector<Command*> cmds;
1442 playlist->rdiff (cmds);
1443 _session->add_commands (cmds);
1445 _session->add_command (new StatefulDiffCommand (playlist));
1450 if ((what_we_got = playlist->cut (time)) != 0) {
1451 _editor.get_cut_buffer().add (what_we_got);
1452 if (Config->get_edit_mode() == Ripple) {
1453 playlist->ripple(time.start(), -time.length(), NULL);
1455 // no need to exclude any regions from rippling here
1457 vector<Command*> cmds;
1458 playlist->rdiff (cmds);
1459 _session->add_commands (cmds);
1461 _session->add_command (new StatefulDiffCommand (playlist));
1465 if ((what_we_got = playlist->copy (time)) != 0) {
1466 _editor.get_cut_buffer().add (what_we_got);
1471 if ((what_we_got = playlist->cut (time)) != 0) {
1472 if (Config->get_edit_mode() == Ripple) {
1473 playlist->ripple(time.start(), -time.length(), NULL);
1475 // no need to exclude any regions from rippling here
1477 vector<Command*> cmds;
1478 playlist->rdiff (cmds);
1479 _session->add_commands (cmds);
1480 _session->add_command (new StatefulDiffCommand (playlist));
1481 what_we_got->release ();
1488 RouteTimeAxisView::paste (samplepos_t pos, const Selection& selection, PasteContext& ctx, const int32_t sub_num)
1494 boost::shared_ptr<Playlist> pl = playlist ();
1495 const ARDOUR::DataType type = pl->data_type();
1496 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1498 if (p == selection.playlists.end()) {
1501 ctx.counts.increase_n_playlists(type);
1503 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1505 /* add multi-paste offset if applicable */
1506 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent();
1507 const samplecnt_t duration = extent.second - extent.first;
1508 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1510 pl->clear_changes ();
1511 pl->clear_owned_changes ();
1512 if (Config->get_edit_mode() == Ripple) {
1513 std::pair<samplepos_t, samplepos_t> extent = (*p)->get_extent_with_endspace();
1514 samplecnt_t amount = extent.second - extent.first;
1515 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1517 pl->paste (*p, pos, ctx.times, sub_num);
1519 vector<Command*> cmds;
1521 _session->add_commands (cmds);
1523 _session->add_command (new StatefulDiffCommand (pl));
1529 struct PlaylistSorter {
1530 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1531 return a->sort_id() < b->sort_id();
1536 RouteTimeAxisView::build_playlist_menu ()
1538 using namespace Menu_Helpers;
1544 delete playlist_action_menu;
1545 playlist_action_menu = new Menu;
1546 playlist_action_menu->set_name ("ArdourContextMenu");
1548 MenuList& playlist_items = playlist_action_menu->items();
1549 playlist_action_menu->set_name ("ArdourContextMenu");
1550 playlist_items.clear();
1552 RadioMenuItem::Group playlist_group;
1553 boost::shared_ptr<Track> tr = track ();
1555 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1557 /* sort the playlists */
1559 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1561 /* add the playlists to the menu */
1562 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1563 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1564 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1565 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1567 if (tr->playlist()->id() == (*i)->id()) {
1572 playlist_items.push_back (SeparatorElem());
1573 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1574 playlist_items.push_back (SeparatorElem());
1576 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1577 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1578 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1581 // Use a label which tells the user what is happening
1582 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1583 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1587 playlist_items.push_back (SeparatorElem());
1588 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1589 playlist_items.push_back (SeparatorElem());
1591 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1595 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1597 assert (is_track());
1599 // exit if we were triggered by deactivating the old playlist
1600 if (!item->get_active()) {
1604 boost::shared_ptr<Playlist> pl (wpl.lock());
1610 if (track()->playlist() == pl) {
1611 // exit when use_playlist is called by the creation of the playlist menu
1612 // or the playlist choice is unchanged
1616 track()->use_playlist (track()->data_type(), pl);
1618 RouteGroup* rg = route_group();
1620 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1621 std::string group_string = "." + rg->name() + ".";
1623 std::string take_name = pl->name();
1624 std::string::size_type idx = take_name.find(group_string);
1626 if (idx == std::string::npos)
1629 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1631 boost::shared_ptr<RouteList> rl (rg->route_list());
1633 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1634 if ((*i) == this->route()) {
1638 std::string playlist_name = (*i)->name()+group_string+take_name;
1640 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1645 if (track->freeze_state() == Track::Frozen) {
1646 /* Don't change playlists of frozen tracks */
1650 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1652 // No playlist for this track for this take yet, make it
1653 track->use_default_new_playlist();
1654 track->playlist()->set_name(playlist_name);
1656 track->use_playlist(track->data_type(), ipl);
1663 RouteTimeAxisView::update_playlist_tip ()
1665 RouteGroup* rg = route_group ();
1666 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::group_select.property_id)) {
1667 string group_string = "." + rg->name() + ".";
1669 string take_name = track()->playlist()->name();
1670 string::size_type idx = take_name.find(group_string);
1672 if (idx != string::npos) {
1673 /* find the bit containing the take number / name */
1674 take_name = take_name.substr (idx + group_string.length());
1676 /* set the playlist button tooltip to the take name */
1679 string_compose(_("Take: %1.%2"),
1680 Gtkmm2ext::markup_escape_text (rg->name()),
1681 Gtkmm2ext::markup_escape_text (take_name))
1688 /* set the playlist button tooltip to the playlist name */
1689 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1694 RouteTimeAxisView::show_playlist_selector ()
1696 _editor.playlist_selector().show_for (this);
1700 RouteTimeAxisView::map_frozen ()
1706 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1708 switch (track()->freeze_state()) {
1710 playlist_button.set_sensitive (false);
1713 playlist_button.set_sensitive (true);
1716 RouteUI::map_frozen ();
1720 RouteTimeAxisView::color_handler ()
1722 //case cTimeStretchOutline:
1723 if (timestretch_rect) {
1724 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1726 //case cTimeStretchFill:
1727 if (timestretch_rect) {
1728 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1734 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1735 * Will add track if necessary.
1738 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1740 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1741 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1744 /* it doesn't exist yet, so we don't care about the button state: just add it */
1745 create_automation_child (param, true);
1748 bool yn = menu->get_active();
1749 bool changed = false;
1751 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1753 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1754 will have done that for us.
1757 if (changed && !no_redraw) {
1765 RouteTimeAxisView::update_pan_track_visibility ()
1767 bool const showit = pan_automation_item->get_active();
1768 bool changed = false;
1770 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1771 if ((*i)->set_marked_for_display (showit)) {
1777 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1782 RouteTimeAxisView::ensure_pan_views (bool show)
1784 bool changed = false;
1785 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1787 (*i)->set_marked_for_display (false);
1790 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1794 if (!_route->panner()) {
1798 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1799 set<Evoral::Parameter>::iterator p;
1801 for (p = params.begin(); p != params.end(); ++p) {
1802 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1804 if (pan_control->parameter().type() == NullAutomation) {
1805 error << "Pan control has NULL automation type!" << endmsg;
1809 if (automation_child (pan_control->parameter ()).get () == 0) {
1811 /* we don't already have an AutomationTimeAxisView for this parameter */
1813 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
1815 boost::shared_ptr<AutomationTimeAxisView> t (
1816 new AutomationTimeAxisView (_session,
1820 pan_control->parameter (),
1828 pan_tracks.push_back (t);
1829 add_automation_child (*p, t, show);
1831 pan_tracks.push_back (automation_child (pan_control->parameter ()));
1838 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1840 if (apply_to_selection) {
1841 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1845 StripableTimeAxisView::show_all_automation ();
1847 /* Show processor automation */
1849 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1850 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1851 if ((*ii)->view == 0) {
1852 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1855 (*ii)->menu_item->set_active (true);
1868 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1870 if (apply_to_selection) {
1871 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1875 StripableTimeAxisView::show_existing_automation ();
1877 /* Show processor automation */
1878 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1879 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1880 if ((*i)->processor->control((*ii)->what)->list()->size() > 0) {
1881 (*ii)->menu_item->set_active (true);
1892 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1894 if (apply_to_selection) {
1895 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1898 StripableTimeAxisView::hide_all_automation ();
1900 /* Hide processor automation */
1901 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1902 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1903 (*ii)->menu_item->set_active (false);
1913 RouteTimeAxisView::region_view_added (RegionView* rv)
1915 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1916 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1917 boost::shared_ptr<AutomationTimeAxisView> atv;
1919 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1924 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1925 (*i)->add_ghost(rv);
1929 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1931 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1937 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1939 parent.remove_processor_automation_node (this);
1943 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1946 remove_child (pan->view);
1950 RouteTimeAxisView::ProcessorAutomationNode*
1951 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1953 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1955 if ((*i)->processor == processor) {
1957 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1958 if ((*ii)->what == what) {
1968 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1970 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1973 ProcessorAutomationNode* pan;
1975 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1976 /* session state may never have been saved with new plugin */
1977 error << _("programming error: ")
1978 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1979 processor->name(), what.type(), (int) what.channel(), what.id() )
1981 abort(); /*NOTREACHED*/
1989 boost::shared_ptr<AutomationControl> control
1990 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1992 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1993 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1994 _editor, *this, false, parent_canvas,
1995 processor->describe_parameter (what), processor->name()));
1997 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1999 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2002 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2007 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2010 pan->menu_item->set_active (false);
2019 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2021 boost::shared_ptr<Processor> processor (p.lock ());
2023 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2024 /* The Amp processor is a special case and is dealt with separately */
2028 set<Evoral::Parameter> existing;
2030 processor->what_has_data (existing);
2032 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2034 Evoral::Parameter param (*i);
2035 boost::shared_ptr<AutomationLine> al;
2037 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2040 add_processor_automation_curve (processor, param);
2046 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2048 boost::shared_ptr<Processor> processor (p.lock ());
2050 if (!processor || !processor->display_to_user ()) {
2054 /* we use this override to veto the Amp processor from the plugin menu,
2055 as its automation lane can be accessed using the special "Fader" menu
2059 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2063 using namespace Menu_Helpers;
2064 ProcessorAutomationInfo *rai;
2065 list<ProcessorAutomationInfo*>::iterator x;
2067 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2069 if (automatable.empty()) {
2073 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2074 if ((*x)->processor == processor) {
2079 if (x == processor_automation.end()) {
2080 rai = new ProcessorAutomationInfo (processor);
2081 processor_automation.push_back (rai);
2086 /* any older menu was deleted at the top of processors_changed()
2087 when we cleared the subplugin menu.
2090 rai->menu = manage (new Menu);
2091 MenuList& items = rai->menu->items();
2092 rai->menu->set_name ("ArdourContextMenu");
2096 std::set<Evoral::Parameter> has_visible_automation;
2097 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2099 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2101 ProcessorAutomationNode* pan;
2102 Gtk::CheckMenuItem* mitem;
2104 string name = processor->describe_parameter (*i);
2106 if (name == X_("hidden")) {
2110 items.push_back (CheckMenuElem (name));
2111 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2113 _subplugin_menu_map[*i] = mitem;
2115 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2116 mitem->set_active(true);
2119 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2123 pan = new ProcessorAutomationNode (*i, mitem, *this);
2125 rai->lines.push_back (pan);
2129 pan->menu_item = mitem;
2133 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2136 if (items.size() == 0) {
2140 /* add the menu for this processor, because the subplugin
2141 menu is always cleared at the top of processors_changed().
2142 this is the result of some poor design in gtkmm and/or
2146 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2151 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2152 RouteTimeAxisView::ProcessorAutomationNode* pan)
2154 bool showit = pan->menu_item->get_active();
2155 bool redraw = false;
2157 if (pan->view == 0 && showit) {
2158 add_processor_automation_curve (rai->processor, pan->what);
2162 if (pan->view && pan->view->set_marked_for_display (showit)) {
2166 if (redraw && !no_redraw) {
2172 RouteTimeAxisView::reread_midnam ()
2174 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (_route->the_instrument ());
2176 bool rv = pi->plugin ()->read_midnam();
2178 if (rv && patch_change_dialog ()) {
2179 patch_change_dialog ()->refresh ();
2184 RouteTimeAxisView::drop_instrument_ref ()
2186 midnam_connection.drop_connections ();
2190 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2193 boost::shared_ptr<Processor> the_instrument (_route->the_instrument());
2194 boost::shared_ptr<PluginInsert> pi = boost::dynamic_pointer_cast<PluginInsert> (the_instrument);
2195 if (pi && pi->plugin ()->has_midnam ()) {
2196 midnam_connection.drop_connections ();
2197 the_instrument->DropReferences.connect (midnam_connection, invalidator (*this),
2198 boost::bind (&RouteTimeAxisView::drop_instrument_ref, this),
2200 pi->plugin()->UpdateMidnam.connect (midnam_connection, invalidator (*this),
2201 boost::bind (&RouteTimeAxisView::reread_midnam, this),
2208 if (c.type == RouteProcessorChange::MeterPointChange) {
2209 /* nothing to do if only the meter point has changed */
2213 using namespace Menu_Helpers;
2215 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2216 (*i)->valid = false;
2219 setup_processor_menu_and_curves ();
2221 bool deleted_processor_automation = false;
2223 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2225 list<ProcessorAutomationInfo*>::iterator tmp;
2233 processor_automation.erase (i);
2234 deleted_processor_automation = true;
2241 if (deleted_processor_automation && !no_redraw) {
2246 boost::shared_ptr<AutomationLine>
2247 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2249 ProcessorAutomationNode* pan;
2251 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2257 return boost::shared_ptr<AutomationLine>();
2261 RouteTimeAxisView::reset_processor_automation_curves ()
2263 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2269 RouteTimeAxisView::can_edit_name () const
2271 /* we do not allow track name changes if it is record enabled
2273 boost::shared_ptr<Track> trk (boost::dynamic_pointer_cast<Track> (_route));
2277 return !trk->rec_enable_control()->get_value();
2281 RouteTimeAxisView::blink_rec_display (bool onoff)
2283 RouteUI::blink_rec_display (onoff);
2287 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2289 if (_ignore_set_layer_display) {
2293 if (apply_to_selection) {
2294 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2298 _view->set_layer_display (d);
2301 set_gui_property (X_("layer-display"), d);
2306 RouteTimeAxisView::layer_display () const
2309 return _view->layer_display ();
2312 /* we don't know, since we don't have a _view, so just return something */
2317 RouteTimeAxisView::fast_update ()
2319 gm.get_level_meter().update_meters ();
2323 RouteTimeAxisView::hide_meter ()
2326 gm.get_level_meter().hide_meters ();
2330 RouteTimeAxisView::show_meter ()
2336 RouteTimeAxisView::reset_meter ()
2338 if (UIConfiguration::instance().get_show_track_meters()) {
2339 int meter_width = 3;
2340 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2343 gm.get_level_meter().setup_meters (height - 9, meter_width);
2350 RouteTimeAxisView::clear_meter ()
2352 gm.get_level_meter().clear_meters ();
2356 RouteTimeAxisView::meter_changed ()
2358 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2360 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2363 // reset peak when meter point changes
2364 gm.reset_peak_display();
2368 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2371 if (_route && !no_redraw) {
2377 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2379 using namespace Menu_Helpers;
2381 if (!_underlay_streams.empty()) {
2382 MenuList& parent_items = parent_menu->items();
2383 Menu* gs_menu = manage (new Menu);
2384 gs_menu->set_name ("ArdourContextMenu");
2385 MenuList& gs_items = gs_menu->items();
2387 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2389 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2390 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2391 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2397 RouteTimeAxisView::set_underlay_state()
2399 if (!underlay_xml_node) {
2403 XMLNodeList nlist = underlay_xml_node->children();
2404 XMLNodeConstIterator niter;
2405 XMLNode *child_node;
2407 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2408 child_node = *niter;
2410 if (child_node->name() != "Underlay") {
2414 XMLProperty const * prop = child_node->property ("id");
2416 PBD::ID id (prop->value());
2418 StripableTimeAxisView* v = _editor.get_stripable_time_axis_by_id (id);
2421 add_underlay(v->view(), false);
2430 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2436 RouteTimeAxisView& other = v->trackview();
2438 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2439 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2440 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2441 abort(); /*NOTREACHED*/
2444 _underlay_streams.push_back(v);
2445 other._underlay_mirrors.push_back(this);
2447 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2449 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2451 if (!underlay_xml_node) {
2452 underlay_xml_node = xml_node->add_child("Underlays");
2455 XMLNode* node = underlay_xml_node->add_child("Underlay");
2456 XMLProperty const * prop = node->add_property("id");
2457 prop->set_value(v->trackview().route()->id().to_s());
2464 RouteTimeAxisView::remove_underlay (StreamView* v)
2470 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2471 RouteTimeAxisView& other = v->trackview();
2473 if (it != _underlay_streams.end()) {
2474 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2476 if (gm == other._underlay_mirrors.end()) {
2477 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2478 abort(); /*NOTREACHED*/
2481 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2483 _underlay_streams.erase(it);
2484 other._underlay_mirrors.erase(gm);
2486 if (underlay_xml_node) {
2487 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2493 RouteTimeAxisView::set_button_names ()
2495 if (_route && _route->solo_safe_control()->solo_safe()) {
2496 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2498 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2500 if (Config->get_solo_control_is_listen_control()) {
2501 switch (Config->get_listen_position()) {
2502 case AfterFaderListen:
2503 solo_button->set_text (S_("AfterFader|A"));
2504 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2506 case PreFaderListen:
2507 solo_button->set_text (S_("PreFader|P"));
2508 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2512 solo_button->set_text (S_("Solo|S"));
2513 set_tooltip (*solo_button, _("Solo"));
2515 mute_button->set_text (S_("Mute|M"));
2519 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2521 Gtk::CheckMenuItem* rv = StripableTimeAxisView::automation_child_menu_item (param);
2526 ParameterMenuMap::iterator i = _subplugin_menu_map.find (param);
2527 if (i != _subplugin_menu_map.end()) {
2535 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2537 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2539 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2543 gain_track.reset (new AutomationTimeAxisView (_session,
2544 _route, _route->amp(), c, param,
2549 _route->amp()->describe_parameter(param)));
2552 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2555 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2559 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2561 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2562 if (!c || ! _route->trim()->active()) {
2566 trim_track.reset (new AutomationTimeAxisView (_session,
2567 _route, _route->trim(), c, param,
2572 _route->trim()->describe_parameter(param)));
2575 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2578 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2582 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2584 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2586 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2590 mute_track.reset (new AutomationTimeAxisView (_session,
2591 _route, _route, c, param,
2596 _route->describe_parameter(param)));
2599 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2602 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2606 void add_region_to_list (RegionView* rv, RegionList* l)
2608 l->push_back (rv->region());
2612 RouteTimeAxisView::combine_regions ()
2614 /* as of may 2011, we do not offer uncombine for MIDI tracks
2617 if (!is_audio_track()) {
2625 RegionList selected_regions;
2626 boost::shared_ptr<Playlist> playlist = track()->playlist();
2628 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2630 if (selected_regions.size() < 2) {
2634 playlist->clear_changes ();
2635 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2637 _session->add_command (new StatefulDiffCommand (playlist));
2638 /* make the new region be selected */
2640 return _view->find_view (compound_region);
2644 RouteTimeAxisView::uncombine_regions ()
2646 /* as of may 2011, we do not offer uncombine for MIDI tracks
2648 if (!is_audio_track()) {
2656 RegionList selected_regions;
2657 boost::shared_ptr<Playlist> playlist = track()->playlist();
2659 /* have to grab selected regions first because the uncombine is going
2660 * to change that in the middle of the list traverse
2663 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2665 playlist->clear_changes ();
2667 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2668 playlist->uncombine (*i);
2671 _session->add_command (new StatefulDiffCommand (playlist));
2675 RouteTimeAxisView::state_id() const
2677 return string_compose ("rtav %1", _route->id().to_s());
2682 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2684 TimeAxisView::remove_child (c);
2686 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2688 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2689 if (i->second == a) {
2690 _automation_tracks.erase (i);
2698 RouteTimeAxisView::color () const
2700 return route_color ();
2704 RouteTimeAxisView::marked_for_display () const
2706 return !_route->presentation_info().hidden();
2710 RouteTimeAxisView::set_marked_for_display (bool yn)
2712 return RouteUI::mark_hidden (!yn);