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);
2341 /* any older menu was deleted at the top of processors_changed()
2342 when we cleared the subplugin menu.
2345 rai->menu = manage (new Menu);
2346 MenuList& items = rai->menu->items();
2347 rai->menu->set_name ("ArdourContextMenu");
2351 std::set<Evoral::Parameter> has_visible_automation;
2352 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2354 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2356 ProcessorAutomationNode* pan;
2357 Gtk::CheckMenuItem* mitem;
2359 string name = processor->describe_parameter (*i);
2361 if (name == X_("hidden")) {
2365 items.push_back (CheckMenuElem (name));
2366 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2368 _subplugin_menu_map[*i] = mitem;
2370 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2371 mitem->set_active(true);
2374 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2378 pan = new ProcessorAutomationNode (*i, mitem, *this);
2380 rai->lines.push_back (pan);
2384 pan->menu_item = mitem;
2388 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2391 if (items.size() > 0) {
2392 processor_automation.push_back (rai);
2398 /* add the menu for this processor, because the subplugin
2399 menu is always cleared at the top of processors_changed().
2400 this is the result of some poor design in gtkmm and/or
2404 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2409 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2410 RouteTimeAxisView::ProcessorAutomationNode* pan)
2412 bool showit = pan->menu_item->get_active();
2413 bool redraw = false;
2415 if (pan->view == 0 && showit) {
2416 add_processor_automation_curve (rai->processor, pan->what);
2420 if (pan->view && pan->view->set_marked_for_display (showit)) {
2424 if (redraw && !no_redraw) {
2430 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2432 if (c.type == RouteProcessorChange::MeterPointChange) {
2433 /* nothing to do if only the meter point has changed */
2437 using namespace Menu_Helpers;
2439 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2440 (*i)->valid = false;
2443 setup_processor_menu_and_curves ();
2445 bool deleted_processor_automation = false;
2447 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2449 list<ProcessorAutomationInfo*>::iterator tmp;
2457 processor_automation.erase (i);
2458 deleted_processor_automation = true;
2465 if (deleted_processor_automation && !no_redraw) {
2470 boost::shared_ptr<AutomationLine>
2471 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2473 ProcessorAutomationNode* pan;
2475 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2481 return boost::shared_ptr<AutomationLine>();
2485 RouteTimeAxisView::reset_processor_automation_curves ()
2487 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2493 RouteTimeAxisView::can_edit_name () const
2495 /* we do not allow track name changes if it is record enabled
2497 return !_route->record_enabled();
2501 RouteTimeAxisView::blink_rec_display (bool onoff)
2503 RouteUI::blink_rec_display (onoff);
2507 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2509 if (_ignore_set_layer_display) {
2513 if (apply_to_selection) {
2514 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2518 _view->set_layer_display (d);
2521 set_gui_property (X_("layer-display"), enum_2_string (d));
2526 RouteTimeAxisView::layer_display () const
2529 return _view->layer_display ();
2532 /* we don't know, since we don't have a _view, so just return something */
2538 boost::shared_ptr<AutomationTimeAxisView>
2539 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2541 AutomationTracks::iterator i = _automation_tracks.find(param);
2542 if (i != _automation_tracks.end()) {
2545 return boost::shared_ptr<AutomationTimeAxisView>();
2550 RouteTimeAxisView::fast_update ()
2552 gm.get_level_meter().update_meters ();
2556 RouteTimeAxisView::hide_meter ()
2559 gm.get_level_meter().hide_meters ();
2563 RouteTimeAxisView::show_meter ()
2569 RouteTimeAxisView::reset_meter ()
2571 if (UIConfiguration::instance().get_show_track_meters()) {
2572 int meter_width = 3;
2573 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2576 gm.get_level_meter().setup_meters (height - 9, meter_width);
2583 RouteTimeAxisView::clear_meter ()
2585 gm.get_level_meter().clear_meters ();
2589 RouteTimeAxisView::meter_changed ()
2591 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2593 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2596 // reset peak when meter point changes
2597 gm.reset_peak_display();
2601 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2604 if (_route && !no_redraw) {
2610 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2612 using namespace Menu_Helpers;
2614 if (!_underlay_streams.empty()) {
2615 MenuList& parent_items = parent_menu->items();
2616 Menu* gs_menu = manage (new Menu);
2617 gs_menu->set_name ("ArdourContextMenu");
2618 MenuList& gs_items = gs_menu->items();
2620 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2622 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2623 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2624 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2630 RouteTimeAxisView::set_underlay_state()
2632 if (!underlay_xml_node) {
2636 XMLNodeList nlist = underlay_xml_node->children();
2637 XMLNodeConstIterator niter;
2638 XMLNode *child_node;
2640 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2641 child_node = *niter;
2643 if (child_node->name() != "Underlay") {
2647 XMLProperty* prop = child_node->property ("id");
2649 PBD::ID id (prop->value());
2651 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2654 add_underlay(v->view(), false);
2663 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2669 RouteTimeAxisView& other = v->trackview();
2671 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2672 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2673 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2674 abort(); /*NOTREACHED*/
2677 _underlay_streams.push_back(v);
2678 other._underlay_mirrors.push_back(this);
2680 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2682 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2684 if (!underlay_xml_node) {
2685 underlay_xml_node = xml_node->add_child("Underlays");
2688 XMLNode* node = underlay_xml_node->add_child("Underlay");
2689 XMLProperty* prop = node->add_property("id");
2690 prop->set_value(v->trackview().route()->id().to_s());
2697 RouteTimeAxisView::remove_underlay (StreamView* v)
2703 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2704 RouteTimeAxisView& other = v->trackview();
2706 if (it != _underlay_streams.end()) {
2707 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2709 if (gm == other._underlay_mirrors.end()) {
2710 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2711 abort(); /*NOTREACHED*/
2714 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2716 _underlay_streams.erase(it);
2717 other._underlay_mirrors.erase(gm);
2719 if (underlay_xml_node) {
2720 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2726 RouteTimeAxisView::set_button_names ()
2728 if (_route && _route->solo_safe()) {
2729 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2731 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2733 if (Config->get_solo_control_is_listen_control()) {
2734 switch (Config->get_listen_position()) {
2735 case AfterFaderListen:
2736 solo_button->set_text (S_("AfterFader|A"));
2737 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2739 case PreFaderListen:
2740 solo_button->set_text (S_("PreFader|P"));
2741 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2745 solo_button->set_text (S_("Solo|S"));
2746 set_tooltip (*solo_button, _("Solo"));
2748 mute_button->set_text (S_("Mute|M"));
2752 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2754 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2755 if (i != _main_automation_menu_map.end()) {
2759 i = _subplugin_menu_map.find (param);
2760 if (i != _subplugin_menu_map.end()) {
2768 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2770 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2772 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2776 gain_track.reset (new AutomationTimeAxisView (_session,
2777 _route, _route->amp(), c, param,
2782 _route->amp()->describe_parameter(param)));
2785 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2788 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2792 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2794 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2795 if (!c || ! _route->trim()->active()) {
2799 trim_track.reset (new AutomationTimeAxisView (_session,
2800 _route, _route->trim(), c, param,
2805 _route->trim()->describe_parameter(param)));
2808 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2811 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2815 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2817 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2819 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2823 mute_track.reset (new AutomationTimeAxisView (_session,
2824 _route, _route, c, param,
2829 _route->describe_parameter(param)));
2832 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2835 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2839 void add_region_to_list (RegionView* rv, RegionList* l)
2841 l->push_back (rv->region());
2845 RouteTimeAxisView::combine_regions ()
2847 /* as of may 2011, we do not offer uncombine for MIDI tracks
2850 if (!is_audio_track()) {
2858 RegionList selected_regions;
2859 boost::shared_ptr<Playlist> playlist = track()->playlist();
2861 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2863 if (selected_regions.size() < 2) {
2867 playlist->clear_changes ();
2868 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2870 _session->add_command (new StatefulDiffCommand (playlist));
2871 /* make the new region be selected */
2873 return _view->find_view (compound_region);
2877 RouteTimeAxisView::uncombine_regions ()
2879 /* as of may 2011, we do not offer uncombine for MIDI tracks
2881 if (!is_audio_track()) {
2889 RegionList selected_regions;
2890 boost::shared_ptr<Playlist> playlist = track()->playlist();
2892 /* have to grab selected regions first because the uncombine is going
2893 * to change that in the middle of the list traverse
2896 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2898 playlist->clear_changes ();
2900 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2901 playlist->uncombine (*i);
2904 _session->add_command (new StatefulDiffCommand (playlist));
2908 RouteTimeAxisView::state_id() const
2910 return string_compose ("rtav %1", _route->id().to_s());
2915 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2917 TimeAxisView::remove_child (c);
2919 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2921 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2922 if (i->second == a) {
2923 _automation_tracks.erase (i);