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 if (_route && !_route->is_master()) {
868 items.push_back (SeparatorElem());
869 items.push_back (MenuElem (_("Duplicate..."), boost::bind (&ARDOUR_UI::start_duplicate_routes, ARDOUR_UI::instance())));
871 items.push_back (SeparatorElem());
872 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(_editor, &PublicEditor::remove_tracks)));
876 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
878 if (apply_to_selection) {
879 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
882 bool needs_bounce = false;
884 if (!track()->can_use_mode (mode, needs_bounce)) {
890 cerr << "would bounce this one\n";
895 track()->set_mode (mode);
900 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
902 TimeAxisView::show_timestretch (start, end, layers, layer);
912 /* check that the time selection was made in our route, or our route group.
913 remember that route_group() == 0 implies the route is *not* in a edit group.
916 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
917 /* this doesn't apply to us */
921 /* ignore it if our edit group is not active */
923 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
928 if (timestretch_rect == 0) {
929 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
930 timestretch_rect->set_fill_color (ArdourCanvas::HSV (UIConfiguration::instance().color ("time stretch fill")).mod (UIConfiguration::instance().modifier ("time stretch fill")).color());
931 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
934 timestretch_rect->show ();
935 timestretch_rect->raise_to_top ();
937 double const x1 = start / _editor.get_current_zoom();
938 double const x2 = (end - 1) / _editor.get_current_zoom();
940 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
941 x2, current_height() * (layers - layer) / layers));
945 RouteTimeAxisView::hide_timestretch ()
947 TimeAxisView::hide_timestretch ();
949 if (timestretch_rect) {
950 timestretch_rect->hide ();
955 RouteTimeAxisView::show_selection (TimeSelection& ts)
959 /* ignore it if our edit group is not active or if the selection was started
960 in some other track or route group (remember that route_group() == 0 means
961 that the track is not in an route group).
964 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
965 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
971 TimeAxisView::show_selection (ts);
975 RouteTimeAxisView::set_height (uint32_t h, TrackHeightMode m)
978 bool height_changed = (height == 0) || (h != height);
981 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
984 gm.get_level_meter().setup_meters (gmlen, meter_width);
986 TimeAxisView::set_height (h, m);
989 _view->set_height ((double) current_height());
992 if (height >= preset_height (HeightNormal)) {
996 gm.get_gain_slider().show();
998 if (!_route || _route->is_monitor()) {
1001 solo_button->show();
1003 if (rec_enable_button)
1004 rec_enable_button->show();
1006 route_group_button.show();
1007 automation_button.show();
1009 if (is_track() && track()->mode() == ARDOUR::Normal) {
1010 playlist_button.show();
1017 gm.get_gain_slider().hide();
1018 mute_button->show();
1019 if (!_route || _route->is_monitor()) {
1020 solo_button->hide();
1022 solo_button->show();
1024 if (rec_enable_button)
1025 rec_enable_button->show();
1027 route_group_button.hide ();
1028 automation_button.hide ();
1030 if (is_track() && track()->mode() == ARDOUR::Normal) {
1031 playlist_button.hide ();
1036 if (height_changed && !no_redraw) {
1037 /* only emit the signal if the height really changed */
1043 RouteTimeAxisView::route_color_changed ()
1046 _view->apply_color (color(), StreamView::RegionColor);
1049 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
1053 RouteTimeAxisView::reset_samples_per_pixel ()
1055 set_samples_per_pixel (_editor.get_current_zoom());
1059 RouteTimeAxisView::set_samples_per_pixel (double fpp)
1064 speed = track()->speed();
1068 _view->set_samples_per_pixel (fpp * speed);
1071 TimeAxisView::set_samples_per_pixel (fpp * speed);
1075 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1077 if (!mitem->get_active()) {
1078 /* this is one of the two calls made when these radio menu items change status. this one
1079 is for the item that became inactive, and we want to ignore it.
1084 if (apply_to_selection) {
1085 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1088 track()->set_align_choice (choice);
1094 RouteTimeAxisView::rename_current_playlist ()
1096 ArdourPrompter prompter (true);
1099 boost::shared_ptr<Track> tr = track();
1100 if (!tr || tr->destructive()) {
1104 boost::shared_ptr<Playlist> pl = tr->playlist();
1109 prompter.set_title (_("Rename Playlist"));
1110 prompter.set_prompt (_("New name for playlist:"));
1111 prompter.set_initial_text (pl->name());
1112 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1113 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1115 switch (prompter.run ()) {
1116 case Gtk::RESPONSE_ACCEPT:
1117 prompter.get_result (name);
1118 if (name.length()) {
1119 pl->set_name (name);
1129 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1131 std::string ret (basename);
1133 std::string const group_string = "." + route_group()->name() + ".";
1135 // iterate through all playlists
1137 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1138 std::string tmp = (*i)->name();
1140 std::string::size_type idx = tmp.find(group_string);
1141 // find those which belong to this group
1142 if (idx != string::npos) {
1143 tmp = tmp.substr(idx + group_string.length());
1145 // and find the largest current number
1147 if (x > maxnumber) {
1156 snprintf (buf, sizeof(buf), "%d", maxnumber);
1158 ret = this->name() + "." + route_group()->name () + "." + buf;
1164 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1168 boost::shared_ptr<Track> tr = track ();
1169 if (!tr || tr->destructive()) {
1173 boost::shared_ptr<const Playlist> pl = tr->playlist();
1180 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1181 name = resolve_new_group_playlist_name(name, playlists_before_op);
1184 while (_session->playlists->by_name(name)) {
1185 name = Playlist::bump_name (name, *_session);
1188 // TODO: The prompter "new" button should be de-activated if the user
1189 // specifies a playlist name which already exists in the session.
1193 ArdourPrompter prompter (true);
1195 prompter.set_title (_("New Copy Playlist"));
1196 prompter.set_prompt (_("Name for new playlist:"));
1197 prompter.set_initial_text (name);
1198 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1199 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1200 prompter.show_all ();
1202 switch (prompter.run ()) {
1203 case Gtk::RESPONSE_ACCEPT:
1204 prompter.get_result (name);
1212 if (name.length()) {
1213 tr->use_copy_playlist ();
1214 tr->playlist()->set_name (name);
1219 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1223 boost::shared_ptr<Track> tr = track ();
1224 if (!tr || tr->destructive()) {
1228 boost::shared_ptr<const Playlist> pl = tr->playlist();
1235 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1236 name = resolve_new_group_playlist_name(name,playlists_before_op);
1239 while (_session->playlists->by_name(name)) {
1240 name = Playlist::bump_name (name, *_session);
1246 ArdourPrompter prompter (true);
1248 prompter.set_title (_("New Playlist"));
1249 prompter.set_prompt (_("Name for new playlist:"));
1250 prompter.set_initial_text (name);
1251 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1252 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1254 switch (prompter.run ()) {
1255 case Gtk::RESPONSE_ACCEPT:
1256 prompter.get_result (name);
1264 if (name.length()) {
1265 tr->use_new_playlist ();
1266 tr->playlist()->set_name (name);
1271 RouteTimeAxisView::clear_playlist ()
1273 boost::shared_ptr<Track> tr = track ();
1274 if (!tr || tr->destructive()) {
1278 boost::shared_ptr<Playlist> pl = tr->playlist();
1283 _editor.clear_playlist (pl);
1287 RouteTimeAxisView::speed_changed ()
1289 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1293 RouteTimeAxisView::update_diskstream_display ()
1303 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1305 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1307 /* special case: select/deselect all tracks */
1309 _editor.begin_reversible_selection_op (X_("Selection Click"));
1311 if (_editor.get_selection().selected (this)) {
1312 _editor.get_selection().clear_tracks ();
1314 _editor.select_all_tracks ();
1317 _editor.commit_reversible_selection_op ();
1322 _editor.begin_reversible_selection_op (X_("Selection Click"));
1324 switch (ArdourKeyboard::selection_type (ev->state)) {
1325 case Selection::Toggle:
1326 _editor.get_selection().toggle (this);
1329 case Selection::Set:
1330 _editor.get_selection().set (this);
1333 case Selection::Extend:
1334 _editor.extend_selection_to_track (*this);
1337 case Selection::Add:
1338 _editor.get_selection().add (this);
1342 _editor.commit_reversible_selection_op ();
1346 RouteTimeAxisView::set_selected_points (PointSelection& points)
1348 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1349 (*i)->set_selected_points (points);
1351 AudioStreamView* asv = dynamic_cast<AudioStreamView*>(_view);
1353 asv->set_selected_points (points);
1358 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1361 _view->set_selected_regionviews (regions);
1365 /** Add the selectable things that we have to a list.
1366 * @param results List to add things to.
1369 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results, bool within)
1374 speed = track()->speed();
1377 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1378 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1380 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1381 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1384 /* pick up visible automation tracks */
1386 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1387 if (!(*i)->hidden()) {
1388 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results, within);
1394 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1397 _view->get_inverted_selectables (sel, results);
1400 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1401 if (!(*i)->hidden()) {
1402 (*i)->get_inverted_selectables (sel, results);
1410 RouteTimeAxisView::route_group () const
1412 return _route->route_group();
1416 RouteTimeAxisView::name() const
1418 return _route->name();
1421 boost::shared_ptr<Playlist>
1422 RouteTimeAxisView::playlist () const
1424 boost::shared_ptr<Track> tr;
1426 if ((tr = track()) != 0) {
1427 return tr->playlist();
1429 return boost::shared_ptr<Playlist> ();
1434 RouteTimeAxisView::name_entry_changed ()
1436 TimeAxisView::name_entry_changed ();
1438 string x = name_entry->get_text ();
1440 if (x == _route->name()) {
1444 strip_whitespace_edges (x);
1446 if (x.length() == 0) {
1447 name_entry->set_text (_route->name());
1451 if (_session->route_name_internal (x)) {
1452 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1454 name_entry->grab_focus ();
1455 } else if (RouteUI::verify_new_route_name (x)) {
1456 _route->set_name (x);
1458 name_entry->grab_focus ();
1462 boost::shared_ptr<Region>
1463 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1465 boost::shared_ptr<Playlist> pl = playlist ();
1468 return pl->find_next_region (pos, point, dir);
1471 return boost::shared_ptr<Region> ();
1475 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1477 boost::shared_ptr<Playlist> pl = playlist ();
1480 return pl->find_next_region_boundary (pos, dir);
1487 RouteTimeAxisView::fade_range (TimeSelection& selection)
1489 boost::shared_ptr<Playlist> what_we_got;
1490 boost::shared_ptr<Track> tr = track ();
1491 boost::shared_ptr<Playlist> playlist;
1494 /* route is a bus, not a track */
1498 playlist = tr->playlist();
1500 TimeSelection time (selection);
1501 float const speed = tr->speed();
1502 if (speed != 1.0f) {
1503 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1504 (*i).start = session_frame_to_track_frame((*i).start, speed);
1505 (*i).end = session_frame_to_track_frame((*i).end, speed);
1509 playlist->clear_changes ();
1510 playlist->clear_owned_changes ();
1512 playlist->fade_range (time);
1514 vector<Command*> cmds;
1515 playlist->rdiff (cmds);
1516 _session->add_commands (cmds);
1517 _session->add_command (new StatefulDiffCommand (playlist));
1522 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1524 boost::shared_ptr<Playlist> what_we_got;
1525 boost::shared_ptr<Track> tr = track ();
1526 boost::shared_ptr<Playlist> playlist;
1529 /* route is a bus, not a track */
1533 playlist = tr->playlist();
1535 TimeSelection time (selection.time);
1536 float const speed = tr->speed();
1537 if (speed != 1.0f) {
1538 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1539 (*i).start = session_frame_to_track_frame((*i).start, speed);
1540 (*i).end = session_frame_to_track_frame((*i).end, speed);
1544 playlist->clear_changes ();
1545 playlist->clear_owned_changes ();
1549 if (playlist->cut (time) != 0) {
1550 if (Config->get_edit_mode() == Ripple)
1551 playlist->ripple(time.start(), -time.length(), NULL);
1552 // no need to exclude any regions from rippling here
1554 vector<Command*> cmds;
1555 playlist->rdiff (cmds);
1556 _session->add_commands (cmds);
1558 _session->add_command (new StatefulDiffCommand (playlist));
1563 if ((what_we_got = playlist->cut (time)) != 0) {
1564 _editor.get_cut_buffer().add (what_we_got);
1565 if (Config->get_edit_mode() == Ripple)
1566 playlist->ripple(time.start(), -time.length(), NULL);
1567 // no need to exclude any regions from rippling here
1569 vector<Command*> cmds;
1570 playlist->rdiff (cmds);
1571 _session->add_commands (cmds);
1573 _session->add_command (new StatefulDiffCommand (playlist));
1577 if ((what_we_got = playlist->copy (time)) != 0) {
1578 _editor.get_cut_buffer().add (what_we_got);
1583 if ((what_we_got = playlist->cut (time)) != 0) {
1584 if (Config->get_edit_mode() == Ripple)
1585 playlist->ripple(time.start(), -time.length(), NULL);
1586 // no need to exclude any regions from rippling here
1588 vector<Command*> cmds;
1589 playlist->rdiff (cmds);
1590 _session->add_commands (cmds);
1591 _session->add_command (new StatefulDiffCommand (playlist));
1592 what_we_got->release ();
1599 RouteTimeAxisView::paste (framepos_t pos, const Selection& selection, PasteContext& ctx)
1605 boost::shared_ptr<Playlist> pl = playlist ();
1606 const ARDOUR::DataType type = pl->data_type();
1607 PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, ctx.counts.n_playlists(type));
1609 if (p == selection.playlists.end()) {
1612 ctx.counts.increase_n_playlists(type);
1614 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1616 if (track()->speed() != 1.0f) {
1617 pos = session_frame_to_track_frame (pos, track()->speed());
1618 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1621 /* add multi-paste offset if applicable */
1622 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1623 const framecnt_t duration = extent.second - extent.first;
1624 pos += _editor.get_paste_offset(pos, ctx.count, duration);
1626 pl->clear_changes ();
1627 pl->clear_owned_changes ();
1628 if (Config->get_edit_mode() == Ripple) {
1629 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1630 framecnt_t amount = extent.second - extent.first;
1631 pl->ripple(pos, amount * ctx.times, boost::shared_ptr<Region>());
1633 pl->paste (*p, pos, ctx.times);
1635 vector<Command*> cmds;
1637 _session->add_commands (cmds);
1639 _session->add_command (new StatefulDiffCommand (pl));
1645 struct PlaylistSorter {
1646 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1647 return a->sort_id() < b->sort_id();
1652 RouteTimeAxisView::build_playlist_menu ()
1654 using namespace Menu_Helpers;
1660 delete playlist_action_menu;
1661 playlist_action_menu = new Menu;
1662 playlist_action_menu->set_name ("ArdourContextMenu");
1664 MenuList& playlist_items = playlist_action_menu->items();
1665 playlist_action_menu->set_name ("ArdourContextMenu");
1666 playlist_items.clear();
1668 RadioMenuItem::Group playlist_group;
1669 boost::shared_ptr<Track> tr = track ();
1671 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1673 /* sort the playlists */
1675 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1677 /* add the playlists to the menu */
1678 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1679 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1680 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1681 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1683 if (tr->playlist()->id() == (*i)->id()) {
1689 playlist_items.push_back (SeparatorElem());
1690 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1691 playlist_items.push_back (SeparatorElem());
1693 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1694 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1695 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1698 // Use a label which tells the user what is happening
1699 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1700 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1704 playlist_items.push_back (SeparatorElem());
1705 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1706 playlist_items.push_back (SeparatorElem());
1708 playlist_items.push_back (MenuElem(_("Select from All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1712 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1714 assert (is_track());
1716 // exit if we were triggered by deactivating the old playlist
1717 if (!item->get_active()) {
1721 boost::shared_ptr<Playlist> pl (wpl.lock());
1727 if (track()->playlist() == pl) {
1728 // exit when use_playlist is called by the creation of the playlist menu
1729 // or the playlist choice is unchanged
1733 track()->use_playlist (pl);
1735 RouteGroup* rg = route_group();
1737 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1738 std::string group_string = "." + rg->name() + ".";
1740 std::string take_name = pl->name();
1741 std::string::size_type idx = take_name.find(group_string);
1743 if (idx == std::string::npos)
1746 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1748 boost::shared_ptr<RouteList> rl (rg->route_list());
1750 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1751 if ((*i) == this->route()) {
1755 std::string playlist_name = (*i)->name()+group_string+take_name;
1757 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1762 if (track->freeze_state() == Track::Frozen) {
1763 /* Don't change playlists of frozen tracks */
1767 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1769 // No playlist for this track for this take yet, make it
1770 track->use_new_playlist();
1771 track->playlist()->set_name(playlist_name);
1773 track->use_playlist(ipl);
1780 RouteTimeAxisView::update_playlist_tip ()
1782 RouteGroup* rg = route_group ();
1783 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1784 string group_string = "." + rg->name() + ".";
1786 string take_name = track()->playlist()->name();
1787 string::size_type idx = take_name.find(group_string);
1789 if (idx != string::npos) {
1790 /* find the bit containing the take number / name */
1791 take_name = take_name.substr (idx + group_string.length());
1793 /* set the playlist button tooltip to the take name */
1796 string_compose(_("Take: %1.%2"),
1797 Gtkmm2ext::markup_escape_text (rg->name()),
1798 Gtkmm2ext::markup_escape_text (take_name))
1805 /* set the playlist button tooltip to the playlist name */
1806 set_tooltip (playlist_button, _("Playlist") + std::string(": ") + Gtkmm2ext::markup_escape_text (track()->playlist()->name()));
1811 RouteTimeAxisView::show_playlist_selector ()
1813 _editor.playlist_selector().show_for (this);
1817 RouteTimeAxisView::map_frozen ()
1823 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1825 switch (track()->freeze_state()) {
1827 playlist_button.set_sensitive (false);
1828 rec_enable_button->set_sensitive (false);
1831 playlist_button.set_sensitive (true);
1832 rec_enable_button->set_sensitive (true);
1838 RouteTimeAxisView::color_handler ()
1840 //case cTimeStretchOutline:
1841 if (timestretch_rect) {
1842 timestretch_rect->set_outline_color (UIConfiguration::instance().color ("time stretch outline"));
1844 //case cTimeStretchFill:
1845 if (timestretch_rect) {
1846 timestretch_rect->set_fill_color (UIConfiguration::instance().color ("time stretch fill"));
1852 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1853 * Will add track if necessary.
1856 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1858 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1859 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1862 /* it doesn't exist yet, so we don't care about the button state: just add it */
1863 create_automation_child (param, true);
1866 bool yn = menu->get_active();
1867 bool changed = false;
1869 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1871 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1872 will have done that for us.
1875 if (changed && !no_redraw) {
1883 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1885 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1891 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1893 if (menu && !_hidden) {
1894 ignore_toggle = true;
1895 menu->set_active (false);
1896 ignore_toggle = false;
1899 if (_route && !no_redraw) {
1905 RouteTimeAxisView::update_gain_track_visibility ()
1907 bool const showit = gain_automation_item->get_active();
1909 if (showit != string_is_affirmative (gain_track->gui_property ("visible"))) {
1910 gain_track->set_marked_for_display (showit);
1912 /* now trigger a redisplay */
1915 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1921 RouteTimeAxisView::update_trim_track_visibility ()
1923 bool const showit = trim_automation_item->get_active();
1925 if (showit != string_is_affirmative (trim_track->gui_property ("visible"))) {
1926 trim_track->set_marked_for_display (showit);
1928 /* now trigger a redisplay */
1931 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1937 RouteTimeAxisView::update_mute_track_visibility ()
1939 bool const showit = mute_automation_item->get_active();
1941 if (showit != string_is_affirmative (mute_track->gui_property ("visible"))) {
1942 mute_track->set_marked_for_display (showit);
1944 /* now trigger a redisplay */
1947 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1953 RouteTimeAxisView::update_pan_track_visibility ()
1955 bool const showit = pan_automation_item->get_active();
1956 bool changed = false;
1958 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1959 if ((*i)->set_marked_for_display (showit)) {
1965 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1970 RouteTimeAxisView::ensure_pan_views (bool show)
1972 bool changed = false;
1973 for (list<boost::shared_ptr<AutomationTimeAxisView> >::iterator i = pan_tracks.begin(); i != pan_tracks.end(); ++i) {
1975 (*i)->set_marked_for_display (false);
1978 _route->gui_changed (X_("visible_tracks"), (void *) 0); /* EMIT_SIGNAL */
1982 if (!_route->panner()) {
1986 set<Evoral::Parameter> params = _route->panner()->what_can_be_automated();
1987 set<Evoral::Parameter>::iterator p;
1989 for (p = params.begin(); p != params.end(); ++p) {
1990 boost::shared_ptr<ARDOUR::AutomationControl> pan_control = _route->pannable()->automation_control(*p);
1992 if (pan_control->parameter().type() == NullAutomation) {
1993 error << "Pan control has NULL automation type!" << endmsg;
1997 if (automation_child (pan_control->parameter ()).get () == 0) {
1999 /* we don't already have an AutomationTimeAxisView for this parameter */
2001 std::string const name = _route->panner()->describe_parameter (pan_control->parameter ());
2003 boost::shared_ptr<AutomationTimeAxisView> t (
2004 new AutomationTimeAxisView (_session,
2008 pan_control->parameter (),
2016 pan_tracks.push_back (t);
2017 add_automation_child (*p, t, show);
2019 pan_tracks.push_back (automation_child (pan_control->parameter ()));
2026 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
2028 if (apply_to_selection) {
2029 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
2033 /* Show our automation */
2035 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2036 i->second->set_marked_for_display (true);
2038 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2041 menu->set_active(true);
2046 /* Show processor automation */
2048 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2049 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2050 if ((*ii)->view == 0) {
2051 add_processor_automation_curve ((*i)->processor, (*ii)->what);
2054 (*ii)->menu_item->set_active (true);
2067 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
2069 if (apply_to_selection) {
2070 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
2074 /* Show our automation */
2076 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2077 if (i->second->has_automation()) {
2078 i->second->set_marked_for_display (true);
2080 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2082 menu->set_active(true);
2087 /* Show processor automation */
2089 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2090 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2091 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
2092 (*ii)->menu_item->set_active (true);
2104 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
2106 if (apply_to_selection) {
2107 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
2111 /* Hide our automation */
2113 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2114 i->second->set_marked_for_display (false);
2116 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
2119 menu->set_active (false);
2123 /* Hide processor automation */
2125 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2126 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2127 (*ii)->menu_item->set_active (false);
2138 RouteTimeAxisView::region_view_added (RegionView* rv)
2140 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
2141 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
2142 boost::shared_ptr<AutomationTimeAxisView> atv;
2144 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
2149 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
2150 (*i)->add_ghost(rv);
2154 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
2156 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
2162 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
2164 parent.remove_processor_automation_node (this);
2168 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
2171 remove_child (pan->view);
2175 RouteTimeAxisView::ProcessorAutomationNode*
2176 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2178 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2180 if ((*i)->processor == processor) {
2182 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
2183 if ((*ii)->what == what) {
2193 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
2195 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2198 ProcessorAutomationNode* pan;
2200 if ((pan = find_processor_automation_node (processor, what)) == 0) {
2201 /* session state may never have been saved with new plugin */
2202 error << _("programming error: ")
2203 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
2204 processor->name(), what.type(), (int) what.channel(), what.id() )
2206 abort(); /*NOTREACHED*/
2214 boost::shared_ptr<AutomationControl> control
2215 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2217 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2218 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2219 _editor, *this, false, parent_canvas,
2220 processor->describe_parameter (what), processor->name()));
2222 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2224 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2227 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2232 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2235 pan->menu_item->set_active (false);
2244 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2246 boost::shared_ptr<Processor> processor (p.lock ());
2248 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2249 /* The Amp processor is a special case and is dealt with separately */
2253 set<Evoral::Parameter> existing;
2255 processor->what_has_data (existing);
2257 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2259 Evoral::Parameter param (*i);
2260 boost::shared_ptr<AutomationLine> al;
2262 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2265 add_processor_automation_curve (processor, param);
2271 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2273 using namespace Menu_Helpers;
2277 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2279 _automation_tracks[param] = track;
2281 /* existing state overrides "show" argument */
2282 string s = track->gui_property ("visible");
2284 show = string_is_affirmative (s);
2287 /* this might or might not change the visibility status, so don't rely on it */
2288 track->set_marked_for_display (show);
2290 if (show && !no_redraw) {
2294 if (!ARDOUR::parameter_is_midi((AutomationType)param.type())) {
2295 /* MIDI-related parameters are always in the menu, there's no
2296 reason to rebuild the menu just because we added a automation
2297 lane for one of them. But if we add a non-MIDI automation
2298 lane, then we need to invalidate the display menu.
2300 delete display_menu;
2306 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2308 boost::shared_ptr<Processor> processor (p.lock ());
2310 if (!processor || !processor->display_to_user ()) {
2314 /* we use this override to veto the Amp processor from the plugin menu,
2315 as its automation lane can be accessed using the special "Fader" menu
2319 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2323 using namespace Menu_Helpers;
2324 ProcessorAutomationInfo *rai;
2325 list<ProcessorAutomationInfo*>::iterator x;
2327 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2329 if (automatable.empty()) {
2333 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2334 if ((*x)->processor == processor) {
2339 if (x == processor_automation.end()) {
2340 rai = new ProcessorAutomationInfo (processor);
2341 processor_automation.push_back (rai);
2346 /* any older menu was deleted at the top of processors_changed()
2347 when we cleared the subplugin menu.
2350 rai->menu = manage (new Menu);
2351 MenuList& items = rai->menu->items();
2352 rai->menu->set_name ("ArdourContextMenu");
2356 std::set<Evoral::Parameter> has_visible_automation;
2357 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2359 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2361 ProcessorAutomationNode* pan;
2362 Gtk::CheckMenuItem* mitem;
2364 string name = processor->describe_parameter (*i);
2366 if (name == X_("hidden")) {
2370 items.push_back (CheckMenuElem (name));
2371 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2373 _subplugin_menu_map[*i] = mitem;
2375 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2376 mitem->set_active(true);
2379 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2383 pan = new ProcessorAutomationNode (*i, mitem, *this);
2385 rai->lines.push_back (pan);
2389 pan->menu_item = mitem;
2393 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2396 if (items.size() == 0) {
2400 /* add the menu for this processor, because the subplugin
2401 menu is always cleared at the top of processors_changed().
2402 this is the result of some poor design in gtkmm and/or
2406 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2411 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2412 RouteTimeAxisView::ProcessorAutomationNode* pan)
2414 bool showit = pan->menu_item->get_active();
2415 bool redraw = false;
2417 if (pan->view == 0 && showit) {
2418 add_processor_automation_curve (rai->processor, pan->what);
2422 if (pan->view && pan->view->set_marked_for_display (showit)) {
2426 if (redraw && !no_redraw) {
2432 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2434 if (c.type == RouteProcessorChange::MeterPointChange) {
2435 /* nothing to do if only the meter point has changed */
2439 using namespace Menu_Helpers;
2441 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2442 (*i)->valid = false;
2445 setup_processor_menu_and_curves ();
2447 bool deleted_processor_automation = false;
2449 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2451 list<ProcessorAutomationInfo*>::iterator tmp;
2459 processor_automation.erase (i);
2460 deleted_processor_automation = true;
2467 if (deleted_processor_automation && !no_redraw) {
2472 boost::shared_ptr<AutomationLine>
2473 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2475 ProcessorAutomationNode* pan;
2477 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2483 return boost::shared_ptr<AutomationLine>();
2487 RouteTimeAxisView::reset_processor_automation_curves ()
2489 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2495 RouteTimeAxisView::can_edit_name () const
2497 /* we do not allow track name changes if it is record enabled
2499 return !_route->record_enabled();
2503 RouteTimeAxisView::blink_rec_display (bool onoff)
2505 RouteUI::blink_rec_display (onoff);
2509 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2511 if (_ignore_set_layer_display) {
2515 if (apply_to_selection) {
2516 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2520 _view->set_layer_display (d);
2523 set_gui_property (X_("layer-display"), enum_2_string (d));
2528 RouteTimeAxisView::layer_display () const
2531 return _view->layer_display ();
2534 /* we don't know, since we don't have a _view, so just return something */
2540 boost::shared_ptr<AutomationTimeAxisView>
2541 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2543 AutomationTracks::iterator i = _automation_tracks.find(param);
2544 if (i != _automation_tracks.end()) {
2547 return boost::shared_ptr<AutomationTimeAxisView>();
2552 RouteTimeAxisView::fast_update ()
2554 gm.get_level_meter().update_meters ();
2558 RouteTimeAxisView::hide_meter ()
2561 gm.get_level_meter().hide_meters ();
2565 RouteTimeAxisView::show_meter ()
2571 RouteTimeAxisView::reset_meter ()
2573 if (UIConfiguration::instance().get_show_track_meters()) {
2574 int meter_width = 3;
2575 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2578 gm.get_level_meter().setup_meters (height - 9, meter_width);
2585 RouteTimeAxisView::clear_meter ()
2587 gm.get_level_meter().clear_meters ();
2591 RouteTimeAxisView::meter_changed ()
2593 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2595 if (_route && !no_redraw && UIConfiguration::instance().get_show_track_meters()) {
2598 // reset peak when meter point changes
2599 gm.reset_peak_display();
2603 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2606 if (_route && !no_redraw) {
2612 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2614 using namespace Menu_Helpers;
2616 if (!_underlay_streams.empty()) {
2617 MenuList& parent_items = parent_menu->items();
2618 Menu* gs_menu = manage (new Menu);
2619 gs_menu->set_name ("ArdourContextMenu");
2620 MenuList& gs_items = gs_menu->items();
2622 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2624 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2625 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2626 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2632 RouteTimeAxisView::set_underlay_state()
2634 if (!underlay_xml_node) {
2638 XMLNodeList nlist = underlay_xml_node->children();
2639 XMLNodeConstIterator niter;
2640 XMLNode *child_node;
2642 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2643 child_node = *niter;
2645 if (child_node->name() != "Underlay") {
2649 XMLProperty* prop = child_node->property ("id");
2651 PBD::ID id (prop->value());
2653 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2656 add_underlay(v->view(), false);
2665 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2671 RouteTimeAxisView& other = v->trackview();
2673 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2674 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2675 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2676 abort(); /*NOTREACHED*/
2679 _underlay_streams.push_back(v);
2680 other._underlay_mirrors.push_back(this);
2682 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2684 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2686 if (!underlay_xml_node) {
2687 underlay_xml_node = xml_node->add_child("Underlays");
2690 XMLNode* node = underlay_xml_node->add_child("Underlay");
2691 XMLProperty* prop = node->add_property("id");
2692 prop->set_value(v->trackview().route()->id().to_s());
2699 RouteTimeAxisView::remove_underlay (StreamView* v)
2705 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2706 RouteTimeAxisView& other = v->trackview();
2708 if (it != _underlay_streams.end()) {
2709 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2711 if (gm == other._underlay_mirrors.end()) {
2712 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2713 abort(); /*NOTREACHED*/
2716 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2718 _underlay_streams.erase(it);
2719 other._underlay_mirrors.erase(gm);
2721 if (underlay_xml_node) {
2722 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2728 RouteTimeAxisView::set_button_names ()
2730 if (_route && _route->solo_safe()) {
2731 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2733 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2735 if (Config->get_solo_control_is_listen_control()) {
2736 switch (Config->get_listen_position()) {
2737 case AfterFaderListen:
2738 solo_button->set_text (S_("AfterFader|A"));
2739 set_tooltip (*solo_button, _("After-fade listen (AFL)"));
2741 case PreFaderListen:
2742 solo_button->set_text (S_("PreFader|P"));
2743 set_tooltip (*solo_button, _("Pre-fade listen (PFL)"));
2747 solo_button->set_text (S_("Solo|S"));
2748 set_tooltip (*solo_button, _("Solo"));
2750 mute_button->set_text (S_("Mute|M"));
2754 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2756 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2757 if (i != _main_automation_menu_map.end()) {
2761 i = _subplugin_menu_map.find (param);
2762 if (i != _subplugin_menu_map.end()) {
2770 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2772 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2774 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2778 gain_track.reset (new AutomationTimeAxisView (_session,
2779 _route, _route->amp(), c, param,
2784 _route->amp()->describe_parameter(param)));
2787 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2790 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2794 RouteTimeAxisView::create_trim_automation_child (const Evoral::Parameter& param, bool show)
2796 boost::shared_ptr<AutomationControl> c = _route->trim()->gain_control();
2797 if (!c || ! _route->trim()->active()) {
2801 trim_track.reset (new AutomationTimeAxisView (_session,
2802 _route, _route->trim(), c, param,
2807 _route->trim()->describe_parameter(param)));
2810 _view->foreach_regionview (sigc::mem_fun (*trim_track.get(), &TimeAxisView::add_ghost));
2813 add_automation_child (Evoral::Parameter(TrimAutomation), trim_track, show);
2817 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2819 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2821 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2825 mute_track.reset (new AutomationTimeAxisView (_session,
2826 _route, _route, c, param,
2831 _route->describe_parameter(param)));
2834 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2837 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2841 void add_region_to_list (RegionView* rv, RegionList* l)
2843 l->push_back (rv->region());
2847 RouteTimeAxisView::combine_regions ()
2849 /* as of may 2011, we do not offer uncombine for MIDI tracks
2852 if (!is_audio_track()) {
2860 RegionList selected_regions;
2861 boost::shared_ptr<Playlist> playlist = track()->playlist();
2863 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2865 if (selected_regions.size() < 2) {
2869 playlist->clear_changes ();
2870 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2872 _session->add_command (new StatefulDiffCommand (playlist));
2873 /* make the new region be selected */
2875 return _view->find_view (compound_region);
2879 RouteTimeAxisView::uncombine_regions ()
2881 /* as of may 2011, we do not offer uncombine for MIDI tracks
2883 if (!is_audio_track()) {
2891 RegionList selected_regions;
2892 boost::shared_ptr<Playlist> playlist = track()->playlist();
2894 /* have to grab selected regions first because the uncombine is going
2895 * to change that in the middle of the list traverse
2898 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2900 playlist->clear_changes ();
2902 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2903 playlist->uncombine (*i);
2906 _session->add_command (new StatefulDiffCommand (playlist));
2910 RouteTimeAxisView::state_id() const
2912 return string_compose ("rtav %1", _route->id().to_s());
2917 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2919 TimeAxisView::remove_child (c);
2921 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2923 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2924 if (i->second == a) {
2925 _automation_tracks.erase (i);