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/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
84 using namespace Gtkmm2ext;
86 using namespace Editing;
90 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
93 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
95 , parent_canvas (canvas)
98 , route_group_button (_("g"))
99 , playlist_button (_("p"))
100 , automation_button (_("a"))
101 , automation_action_menu (0)
102 , plugins_submenu_item (0)
103 , route_group_menu (0)
104 , playlist_action_menu (0)
106 , color_mode_menu (0)
107 , gm (sess, true, 125, 18)
108 , _ignore_set_layer_display (false)
113 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
115 RouteUI::set_route (rt);
117 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
118 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
119 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
122 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
125 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
126 gm.get_level_meter().set_no_show_all();
127 gm.get_level_meter().setup_meters(50, meter_width);
128 gm.update_gain_sensitive ();
130 string str = gui_property ("height");
132 set_height (atoi (str));
134 set_height (preset_height (HeightNormal));
137 if (!_route->is_auditioner()) {
138 if (gui_property ("visible").empty()) {
139 set_gui_property ("visible", true);
142 set_gui_property ("visible", false);
146 update_solo_display ();
148 timestretch_rect = 0;
151 ignore_toggle = false;
153 route_group_button.set_name ("route button");
154 playlist_button.set_name ("route button");
155 automation_button.set_name ("route button");
157 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
158 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
159 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
165 switch (track()->mode()) {
167 case ARDOUR::NonLayered:
168 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
170 case ARDOUR::Destructive:
171 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
175 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
177 if (is_midi_track()) {
178 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
179 gm.set_fader_name ("MidiTrackFader");
181 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
182 gm.set_fader_name ("AudioTrackFader");
185 rec_enable_button->set_sensitive (_session->writable());
187 /* set playlist button tip to the current playlist, and make it update when it changes */
188 update_playlist_tip ();
189 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
192 gm.set_fader_name ("AudioBusFader");
195 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
196 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
197 controls_hbox.pack_start(*mtrbox, false, false, 4);
200 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
201 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
202 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 if (!_route->is_master()) {
207 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
210 if (!ARDOUR::Profile->get_trx()) {
211 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
212 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
215 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
216 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
217 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
219 if (is_midi_track()) {
220 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
222 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
227 if (!ARDOUR::Profile->get_trx()) {
228 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
231 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
232 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
237 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
238 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
242 str = gui_property ("layer-display");
244 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
247 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
248 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
250 /* pick up the correct freeze state */
255 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
256 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
258 PropertyList* plist = new PropertyList();
260 plist->add (ARDOUR::Properties::mute, true);
261 plist->add (ARDOUR::Properties::solo, true);
263 route_group_menu = new RouteGroupMenu (_session, plist);
265 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
267 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
270 RouteTimeAxisView::~RouteTimeAxisView ()
272 CatchDeletion (this);
274 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
278 delete playlist_action_menu;
279 playlist_action_menu = 0;
284 _automation_tracks.clear ();
286 delete route_group_menu;
290 RouteTimeAxisView::post_construct ()
292 /* map current state of the route */
294 update_diskstream_display ();
295 setup_processor_menu_and_curves ();
296 reset_processor_automation_curves ();
299 /** Set up the processor menu for the current set of processors, and
300 * display automation curves for any parameters which have data.
303 RouteTimeAxisView::setup_processor_menu_and_curves ()
305 _subplugin_menu_map.clear ();
306 subplugin_menu.items().clear ();
307 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
308 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
312 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
314 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
315 if (_route->route_group()) {
316 _route->route_group()->remove (_route);
322 r.push_back (route ());
324 route_group_menu->build (r);
325 route_group_menu->menu()->popup (ev->button, ev->time);
331 RouteTimeAxisView::playlist_changed ()
337 RouteTimeAxisView::label_view ()
339 string x = _route->name();
341 if (x != name_label.get_text()) {
342 name_label.set_text (x);
348 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
350 if (what_changed.contains (ARDOUR::Properties::name)) {
356 RouteTimeAxisView::take_name_changed (void *src)
364 RouteTimeAxisView::playlist_click ()
366 build_playlist_menu ();
367 conditionally_add_to_selection ();
368 playlist_action_menu->popup (1, gtk_get_current_event_time());
372 RouteTimeAxisView::automation_click ()
374 conditionally_add_to_selection ();
375 build_automation_action_menu (false);
376 automation_action_menu->popup (1, gtk_get_current_event_time());
380 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
382 using namespace Menu_Helpers;
384 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
385 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
388 detach_menu (subplugin_menu);
390 _main_automation_menu_map.clear ();
391 delete automation_action_menu;
392 automation_action_menu = new Menu;
394 MenuList& items = automation_action_menu->items();
396 automation_action_menu->set_name ("ArdourContextMenu");
398 items.push_back (MenuElem (_("Show All Automation"),
399 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
401 items.push_back (MenuElem (_("Show Existing Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
404 items.push_back (MenuElem (_("Hide All Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above
411 if (!subplugin_menu.items().empty()) {
412 items.push_back (SeparatorElem ());
413 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
414 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
419 RouteTimeAxisView::build_display_menu ()
421 using namespace Menu_Helpers;
425 TimeAxisView::build_display_menu ();
427 /* now fill it with our stuff */
429 MenuList& items = display_menu->items();
430 display_menu->set_name ("ArdourContextMenu");
432 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
435 detach_menu (*_size_menu);
438 items.push_back (MenuElem (_("Height"), *_size_menu));
440 items.push_back (SeparatorElem());
442 if (!Profile->get_sae()) {
443 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
444 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
445 items.push_back (SeparatorElem());
448 // Hook for derived classes to add type specific stuff
449 append_extra_display_menu_items ();
453 Menu* layers_menu = manage (new Menu);
454 MenuList &layers_items = layers_menu->items();
455 layers_menu->set_name("ArdourContextMenu");
457 RadioMenuItem::Group layers_group;
459 /* Find out how many overlaid/stacked tracks we have in the selection */
463 TrackSelection const & s = _editor.get_selection().tracks;
464 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
465 StreamView* v = (*i)->view ();
470 switch (v->layer_display ()) {
481 /* We're not connecting to signal_toggled() here; in the case where these two items are
482 set to be in the `inconsistent' state, it seems that one or other will end up active
483 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
484 select the active one, no toggled signal is emitted so nothing happens.
487 _ignore_set_layer_display = true;
489 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
490 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
491 i->set_active (overlaid != 0 && stacked == 0);
492 i->set_inconsistent (overlaid != 0 && stacked != 0);
493 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
495 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
496 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
497 i->set_active (overlaid == 0 && stacked != 0);
498 i->set_inconsistent (overlaid != 0 && stacked != 0);
499 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
501 _ignore_set_layer_display = false;
503 items.push_back (MenuElem (_("Layers"), *layers_menu));
505 if (!Profile->get_sae()) {
507 Menu* alignment_menu = manage (new Menu);
508 MenuList& alignment_items = alignment_menu->items();
509 alignment_menu->set_name ("ArdourContextMenu");
511 RadioMenuItem::Group align_group;
513 /* Same verbose hacks as for the layering options above */
519 boost::shared_ptr<Track> first_track;
521 TrackSelection const & s = _editor.get_selection().tracks;
522 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
523 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
524 if (!r || !r->is_track ()) {
529 first_track = r->track();
532 switch (r->track()->alignment_choice()) {
536 switch (r->track()->alignment_style()) {
537 case ExistingMaterial:
545 case UseExistingMaterial:
561 inconsistent = false;
570 if (!inconsistent && first_track) {
572 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
573 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
574 i->set_active (automatic != 0 && existing == 0 && capture == 0);
575 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
577 switch (first_track->alignment_choice()) {
579 switch (first_track->alignment_style()) {
580 case ExistingMaterial:
581 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
584 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
592 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
593 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
594 i->set_active (existing != 0 && capture == 0 && automatic == 0);
595 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
597 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
598 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
599 i->set_active (existing == 0 && capture != 0 && automatic == 0);
600 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
602 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
608 Menu* mode_menu = manage (new Menu);
609 MenuList& mode_items = mode_menu->items ();
610 mode_menu->set_name ("ArdourContextMenu");
612 RadioMenuItem::Group mode_group;
618 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
619 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
620 if (!r || !r->is_track ()) {
624 switch (r->track()->mode()) {
637 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
638 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
639 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
640 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
641 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
643 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
644 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
645 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
646 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
647 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
649 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
650 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
651 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
652 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
653 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
655 items.push_back (MenuElem (_("Mode"), *mode_menu));
659 items.push_back (SeparatorElem());
661 build_playlist_menu ();
662 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
663 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
666 route_group_menu->detach ();
669 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
670 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
672 r.push_back (rtv->route ());
677 r.push_back (route ());
680 route_group_menu->build (r);
681 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
683 build_automation_action_menu (true);
684 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
686 items.push_back (SeparatorElem());
690 TrackSelection const & s = _editor.get_selection().tracks;
691 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
692 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
697 if (r->route()->active()) {
704 items.push_back (CheckMenuElem (_("Active")));
705 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
706 bool click_sets_active = true;
707 if (active > 0 && inactive == 0) {
708 i->set_active (true);
709 click_sets_active = false;
710 } else if (active > 0 && inactive > 0) {
711 i->set_inconsistent (true);
713 i->set_sensitive(! _session->transport_rolling());
714 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
716 items.push_back (SeparatorElem());
717 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
718 if (!Profile->get_sae()) {
719 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
721 items.push_front (SeparatorElem());
722 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
727 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
729 if (apply_to_selection) {
730 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
733 bool needs_bounce = false;
735 if (!track()->can_use_mode (mode, needs_bounce)) {
741 cerr << "would bounce this one\n";
746 track()->set_mode (mode);
748 rec_enable_button->remove ();
751 case ARDOUR::NonLayered:
753 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
754 rec_enable_button->set_text (string());
756 case ARDOUR::Destructive:
757 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
758 rec_enable_button->set_text (string());
762 rec_enable_button->show_all ();
767 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
769 TimeAxisView::show_timestretch (start, end, layers, layer);
779 /* check that the time selection was made in our route, or our route group.
780 remember that route_group() == 0 implies the route is *not* in a edit group.
783 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
784 /* this doesn't apply to us */
788 /* ignore it if our edit group is not active */
790 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
795 if (timestretch_rect == 0) {
796 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
797 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
798 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
801 timestretch_rect->show ();
802 timestretch_rect->raise_to_top ();
804 double const x1 = start / _editor.get_current_zoom();
805 double const x2 = (end - 1) / _editor.get_current_zoom();
807 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
808 x2, current_height() * (layers - layer) / layers));
812 RouteTimeAxisView::hide_timestretch ()
814 TimeAxisView::hide_timestretch ();
816 if (timestretch_rect) {
817 timestretch_rect->hide ();
822 RouteTimeAxisView::show_selection (TimeSelection& ts)
826 /* ignore it if our edit group is not active or if the selection was started
827 in some other track or route group (remember that route_group() == 0 means
828 that the track is not in an route group).
831 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
832 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
838 TimeAxisView::show_selection (ts);
842 RouteTimeAxisView::set_height (uint32_t h)
845 bool height_changed = (height == 0) || (h != height);
848 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
851 gm.get_level_meter().setup_meters (gmlen, meter_width);
853 TimeAxisView::set_height (h);
856 _view->set_height ((double) current_height());
859 if (height >= preset_height (HeightNormal)) {
863 gm.get_gain_slider().show();
865 if (!_route || _route->is_monitor()) {
870 if (rec_enable_button)
871 rec_enable_button->show();
873 route_group_button.show();
874 automation_button.show();
876 if (is_track() && track()->mode() == ARDOUR::Normal) {
877 playlist_button.show();
884 gm.get_gain_slider().hide();
886 if (!_route || _route->is_monitor()) {
891 if (rec_enable_button)
892 rec_enable_button->show();
894 route_group_button.hide ();
895 automation_button.hide ();
897 if (is_track() && track()->mode() == ARDOUR::Normal) {
898 playlist_button.hide ();
903 if (height_changed && !no_redraw) {
904 /* only emit the signal if the height really changed */
910 RouteTimeAxisView::route_color_changed ()
913 _view->apply_color (color(), StreamView::RegionColor);
918 RouteTimeAxisView::reset_samples_per_pixel ()
920 set_samples_per_pixel (_editor.get_current_zoom());
924 RouteTimeAxisView::set_samples_per_pixel (double fpp)
929 speed = track()->speed();
933 _view->set_samples_per_pixel (fpp * speed);
936 TimeAxisView::set_samples_per_pixel (fpp * speed);
940 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
942 if (!mitem->get_active()) {
943 /* this is one of the two calls made when these radio menu items change status. this one
944 is for the item that became inactive, and we want to ignore it.
949 if (apply_to_selection) {
950 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
953 track()->set_align_choice (choice);
959 RouteTimeAxisView::rename_current_playlist ()
961 ArdourPrompter prompter (true);
964 boost::shared_ptr<Track> tr = track();
965 if (!tr || tr->destructive()) {
969 boost::shared_ptr<Playlist> pl = tr->playlist();
974 prompter.set_title (_("Rename Playlist"));
975 prompter.set_prompt (_("New name for playlist:"));
976 prompter.set_initial_text (pl->name());
977 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
978 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
980 switch (prompter.run ()) {
981 case Gtk::RESPONSE_ACCEPT:
982 prompter.get_result (name);
994 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
996 std::string ret (basename);
998 std::string const group_string = "." + route_group()->name() + ".";
1000 // iterate through all playlists
1002 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1003 std::string tmp = (*i)->name();
1005 std::string::size_type idx = tmp.find(group_string);
1006 // find those which belong to this group
1007 if (idx != string::npos) {
1008 tmp = tmp.substr(idx + group_string.length());
1010 // and find the largest current number
1012 if (x > maxnumber) {
1021 snprintf (buf, sizeof(buf), "%d", maxnumber);
1023 ret = this->name() + "." + route_group()->name () + "." + buf;
1029 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1033 boost::shared_ptr<Track> tr = track ();
1034 if (!tr || tr->destructive()) {
1038 boost::shared_ptr<const Playlist> pl = tr->playlist();
1045 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1046 name = resolve_new_group_playlist_name(name, playlists_before_op);
1049 while (_session->playlists->by_name(name)) {
1050 name = Playlist::bump_name (name, *_session);
1053 // TODO: The prompter "new" button should be de-activated if the user
1054 // specifies a playlist name which already exists in the session.
1058 ArdourPrompter prompter (true);
1060 prompter.set_title (_("New Copy Playlist"));
1061 prompter.set_prompt (_("Name for new playlist:"));
1062 prompter.set_initial_text (name);
1063 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1064 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1065 prompter.show_all ();
1067 switch (prompter.run ()) {
1068 case Gtk::RESPONSE_ACCEPT:
1069 prompter.get_result (name);
1077 if (name.length()) {
1078 tr->use_copy_playlist ();
1079 tr->playlist()->set_name (name);
1084 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1088 boost::shared_ptr<Track> tr = track ();
1089 if (!tr || tr->destructive()) {
1093 boost::shared_ptr<const Playlist> pl = tr->playlist();
1100 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1101 name = resolve_new_group_playlist_name(name,playlists_before_op);
1104 while (_session->playlists->by_name(name)) {
1105 name = Playlist::bump_name (name, *_session);
1111 ArdourPrompter prompter (true);
1113 prompter.set_title (_("New Playlist"));
1114 prompter.set_prompt (_("Name for new playlist:"));
1115 prompter.set_initial_text (name);
1116 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1117 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1119 switch (prompter.run ()) {
1120 case Gtk::RESPONSE_ACCEPT:
1121 prompter.get_result (name);
1129 if (name.length()) {
1130 tr->use_new_playlist ();
1131 tr->playlist()->set_name (name);
1136 RouteTimeAxisView::clear_playlist ()
1138 boost::shared_ptr<Track> tr = track ();
1139 if (!tr || tr->destructive()) {
1143 boost::shared_ptr<Playlist> pl = tr->playlist();
1148 _editor.clear_playlist (pl);
1152 RouteTimeAxisView::speed_changed ()
1154 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1158 RouteTimeAxisView::update_diskstream_display ()
1168 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1170 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1172 /* special case: select/deselect all tracks */
1173 if (_editor.get_selection().selected (this)) {
1174 _editor.get_selection().clear_tracks ();
1176 _editor.select_all_tracks ();
1182 switch (ArdourKeyboard::selection_type (ev->state)) {
1183 case Selection::Toggle:
1184 _editor.get_selection().toggle (this);
1187 case Selection::Set:
1188 _editor.get_selection().set (this);
1191 case Selection::Extend:
1192 _editor.extend_selection_to_track (*this);
1195 case Selection::Add:
1196 _editor.get_selection().add (this);
1202 RouteTimeAxisView::set_selected_points (PointSelection& points)
1204 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1205 (*i)->set_selected_points (points);
1210 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1213 _view->set_selected_regionviews (regions);
1217 /** Add the selectable things that we have to a list.
1218 * @param results List to add things to.
1221 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1226 speed = track()->speed();
1229 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1230 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1232 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1233 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1236 /* pick up visible automation tracks */
1238 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1239 if (!(*i)->hidden()) {
1240 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1246 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1249 _view->get_inverted_selectables (sel, results);
1252 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1253 if (!(*i)->hidden()) {
1254 (*i)->get_inverted_selectables (sel, results);
1262 RouteTimeAxisView::route_group () const
1264 return _route->route_group();
1268 RouteTimeAxisView::name() const
1270 return _route->name();
1273 boost::shared_ptr<Playlist>
1274 RouteTimeAxisView::playlist () const
1276 boost::shared_ptr<Track> tr;
1278 if ((tr = track()) != 0) {
1279 return tr->playlist();
1281 return boost::shared_ptr<Playlist> ();
1286 RouteTimeAxisView::name_entry_changed ()
1288 TimeAxisView::name_entry_changed ();
1290 string x = name_entry->get_text ();
1292 if (x == _route->name()) {
1296 strip_whitespace_edges (x);
1298 if (x.length() == 0) {
1299 name_entry->set_text (_route->name());
1303 if (_session->route_name_internal (x)) {
1304 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1306 name_entry->grab_focus ();
1307 } else if (RouteUI::verify_new_route_name (x)) {
1308 _route->set_name (x);
1310 name_entry->grab_focus ();
1314 boost::shared_ptr<Region>
1315 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1317 boost::shared_ptr<Playlist> pl = playlist ();
1320 return pl->find_next_region (pos, point, dir);
1323 return boost::shared_ptr<Region> ();
1327 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1329 boost::shared_ptr<Playlist> pl = playlist ();
1332 return pl->find_next_region_boundary (pos, dir);
1339 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1341 boost::shared_ptr<Playlist> what_we_got;
1342 boost::shared_ptr<Track> tr = track ();
1343 boost::shared_ptr<Playlist> playlist;
1346 /* route is a bus, not a track */
1350 playlist = tr->playlist();
1352 TimeSelection time (selection.time);
1353 float const speed = tr->speed();
1354 if (speed != 1.0f) {
1355 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1356 (*i).start = session_frame_to_track_frame((*i).start, speed);
1357 (*i).end = session_frame_to_track_frame((*i).end, speed);
1361 playlist->clear_changes ();
1362 playlist->clear_owned_changes ();
1366 if (playlist->cut (time) != 0) {
1367 vector<Command*> cmds;
1368 playlist->rdiff (cmds);
1369 _session->add_commands (cmds);
1371 _session->add_command (new StatefulDiffCommand (playlist));
1376 if ((what_we_got = playlist->cut (time)) != 0) {
1377 _editor.get_cut_buffer().add (what_we_got);
1378 vector<Command*> cmds;
1379 playlist->rdiff (cmds);
1380 _session->add_commands (cmds);
1382 _session->add_command (new StatefulDiffCommand (playlist));
1386 if ((what_we_got = playlist->copy (time)) != 0) {
1387 _editor.get_cut_buffer().add (what_we_got);
1392 if ((what_we_got = playlist->cut (time)) != 0) {
1394 vector<Command*> cmds;
1395 playlist->rdiff (cmds);
1396 _session->add_commands (cmds);
1397 _session->add_command (new StatefulDiffCommand (playlist));
1398 what_we_got->release ();
1405 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1411 boost::shared_ptr<Playlist> pl = playlist ();
1412 PlaylistSelection::iterator p;
1414 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1416 if (p == selection.playlists.end()) {
1420 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1422 if (track()->speed() != 1.0f) {
1423 pos = session_frame_to_track_frame (pos, track()->speed());
1424 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1427 pl->clear_changes ();
1428 pl->paste (*p, pos, times);
1429 _session->add_command (new StatefulDiffCommand (pl));
1435 struct PlaylistSorter {
1436 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1437 return a->sort_id() < b->sort_id();
1442 RouteTimeAxisView::build_playlist_menu ()
1444 using namespace Menu_Helpers;
1450 delete playlist_action_menu;
1451 playlist_action_menu = new Menu;
1452 playlist_action_menu->set_name ("ArdourContextMenu");
1454 MenuList& playlist_items = playlist_action_menu->items();
1455 playlist_action_menu->set_name ("ArdourContextMenu");
1456 playlist_items.clear();
1458 RadioMenuItem::Group playlist_group;
1459 boost::shared_ptr<Track> tr = track ();
1461 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1463 /* sort the playlists */
1465 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1467 /* add the playlists to the menu */
1468 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1469 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1470 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1471 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1473 if (tr->playlist()->id() == (*i)->id()) {
1479 playlist_items.push_back (SeparatorElem());
1480 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1481 playlist_items.push_back (SeparatorElem());
1483 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1484 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1485 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1488 // Use a label which tells the user what is happening
1489 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1490 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1494 playlist_items.push_back (SeparatorElem());
1495 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1496 playlist_items.push_back (SeparatorElem());
1498 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1502 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1504 assert (is_track());
1506 // exit if we were triggered by deactivating the old playlist
1507 if (!item->get_active()) {
1511 boost::shared_ptr<Playlist> pl (wpl.lock());
1517 if (track()->playlist() == pl) {
1518 // exit when use_playlist is called by the creation of the playlist menu
1519 // or the playlist choice is unchanged
1523 track()->use_playlist (pl);
1525 RouteGroup* rg = route_group();
1527 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1528 std::string group_string = "." + rg->name() + ".";
1530 std::string take_name = pl->name();
1531 std::string::size_type idx = take_name.find(group_string);
1533 if (idx == std::string::npos)
1536 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1538 boost::shared_ptr<RouteList> rl (rg->route_list());
1540 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1541 if ((*i) == this->route()) {
1545 std::string playlist_name = (*i)->name()+group_string+take_name;
1547 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1552 if (track->freeze_state() == Track::Frozen) {
1553 /* Don't change playlists of frozen tracks */
1557 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1559 // No playlist for this track for this take yet, make it
1560 track->use_new_playlist();
1561 track->playlist()->set_name(playlist_name);
1563 track->use_playlist(ipl);
1570 RouteTimeAxisView::update_playlist_tip ()
1572 RouteGroup* rg = route_group ();
1573 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1574 string group_string = "." + rg->name() + ".";
1576 string take_name = track()->playlist()->name();
1577 string::size_type idx = take_name.find(group_string);
1579 if (idx != string::npos) {
1580 /* find the bit containing the take number / name */
1581 take_name = take_name.substr (idx + group_string.length());
1583 /* set the playlist button tooltip to the take name */
1584 ARDOUR_UI::instance()->set_tip (
1586 string_compose(_("Take: %1.%2"),
1587 Glib::Markup::escape_text(rg->name()),
1588 Glib::Markup::escape_text(take_name))
1595 /* set the playlist button tooltip to the playlist name */
1596 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1601 RouteTimeAxisView::show_playlist_selector ()
1603 _editor.playlist_selector().show_for (this);
1607 RouteTimeAxisView::map_frozen ()
1613 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1615 switch (track()->freeze_state()) {
1617 playlist_button.set_sensitive (false);
1618 rec_enable_button->set_sensitive (false);
1621 playlist_button.set_sensitive (true);
1622 rec_enable_button->set_sensitive (true);
1628 RouteTimeAxisView::color_handler ()
1630 //case cTimeStretchOutline:
1631 if (timestretch_rect) {
1632 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1634 //case cTimeStretchFill:
1635 if (timestretch_rect) {
1636 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1642 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1643 * Will add track if necessary.
1646 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1648 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1649 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1652 /* it doesn't exist yet, so we don't care about the button state: just add it */
1653 create_automation_child (param, true);
1656 bool yn = menu->get_active();
1657 bool changed = false;
1659 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1661 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1662 will have done that for us.
1665 if (changed && !no_redraw) {
1673 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1675 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1681 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1683 if (menu && !_hidden) {
1684 ignore_toggle = true;
1685 menu->set_active (false);
1686 ignore_toggle = false;
1689 if (_route && !no_redraw) {
1696 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1698 if (apply_to_selection) {
1699 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1703 /* Show our automation */
1705 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1706 i->second->set_marked_for_display (true);
1708 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1711 menu->set_active(true);
1716 /* Show processor automation */
1718 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1719 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1720 if ((*ii)->view == 0) {
1721 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1724 (*ii)->menu_item->set_active (true);
1737 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1739 if (apply_to_selection) {
1740 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1744 /* Show our automation */
1746 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1747 if (i->second->has_automation()) {
1748 i->second->set_marked_for_display (true);
1750 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1752 menu->set_active(true);
1757 /* Show processor automation */
1759 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1760 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1761 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1762 (*ii)->menu_item->set_active (true);
1774 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1776 if (apply_to_selection) {
1777 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1781 /* Hide our automation */
1783 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1784 i->second->set_marked_for_display (false);
1786 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1789 menu->set_active (false);
1793 /* Hide processor automation */
1795 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1796 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1797 (*ii)->menu_item->set_active (false);
1808 RouteTimeAxisView::region_view_added (RegionView* rv)
1810 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1811 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1812 boost::shared_ptr<AutomationTimeAxisView> atv;
1814 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1819 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1820 (*i)->add_ghost(rv);
1824 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1826 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1832 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1834 parent.remove_processor_automation_node (this);
1838 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1841 remove_child (pan->view);
1845 RouteTimeAxisView::ProcessorAutomationNode*
1846 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1848 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1850 if ((*i)->processor == processor) {
1852 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1853 if ((*ii)->what == what) {
1863 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1865 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1868 ProcessorAutomationNode* pan;
1870 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1871 /* session state may never have been saved with new plugin */
1872 error << _("programming error: ")
1873 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1874 processor->name(), what.type(), (int) what.channel(), what.id() )
1884 boost::shared_ptr<AutomationControl> control
1885 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1887 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1888 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1889 _editor, *this, false, parent_canvas,
1890 processor->describe_parameter (what), processor->name()));
1892 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1894 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1897 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1902 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1905 pan->menu_item->set_active (false);
1914 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1916 boost::shared_ptr<Processor> processor (p.lock ());
1918 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1919 /* The Amp processor is a special case and is dealt with separately */
1923 set<Evoral::Parameter> existing;
1925 processor->what_has_data (existing);
1927 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1929 Evoral::Parameter param (*i);
1930 boost::shared_ptr<AutomationLine> al;
1932 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1935 add_processor_automation_curve (processor, param);
1941 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1943 using namespace Menu_Helpers;
1947 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1949 _automation_tracks[param] = track;
1951 /* existing state overrides "show" argument */
1952 string s = track->gui_property ("visible");
1954 show = string_is_affirmative (s);
1957 /* this might or might not change the visibility status, so don't rely on it */
1958 track->set_marked_for_display (show);
1960 if (show && !no_redraw) {
1964 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1965 /* MIDI-related parameters are always in the menu, there's no
1966 reason to rebuild the menu just because we added a automation
1967 lane for one of them. But if we add a non-MIDI automation
1968 lane, then we need to invalidate the display menu.
1970 delete display_menu;
1976 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1978 boost::shared_ptr<Processor> processor (p.lock ());
1980 if (!processor || !processor->display_to_user ()) {
1984 /* we use this override to veto the Amp processor from the plugin menu,
1985 as its automation lane can be accessed using the special "Fader" menu
1989 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1993 using namespace Menu_Helpers;
1994 ProcessorAutomationInfo *rai;
1995 list<ProcessorAutomationInfo*>::iterator x;
1997 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1999 if (automatable.empty()) {
2003 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2004 if ((*x)->processor == processor) {
2009 if (x == processor_automation.end()) {
2011 rai = new ProcessorAutomationInfo (processor);
2012 processor_automation.push_back (rai);
2020 /* any older menu was deleted at the top of processors_changed()
2021 when we cleared the subplugin menu.
2024 rai->menu = manage (new Menu);
2025 MenuList& items = rai->menu->items();
2026 rai->menu->set_name ("ArdourContextMenu");
2030 std::set<Evoral::Parameter> has_visible_automation;
2031 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2033 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2035 ProcessorAutomationNode* pan;
2036 Gtk::CheckMenuItem* mitem;
2038 string name = processor->describe_parameter (*i);
2040 items.push_back (CheckMenuElem (name));
2041 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2043 _subplugin_menu_map[*i] = mitem;
2045 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2046 mitem->set_active(true);
2049 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2053 pan = new ProcessorAutomationNode (*i, mitem, *this);
2055 rai->lines.push_back (pan);
2059 pan->menu_item = mitem;
2063 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2066 /* add the menu for this processor, because the subplugin
2067 menu is always cleared at the top of processors_changed().
2068 this is the result of some poor design in gtkmm and/or
2072 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2077 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2078 RouteTimeAxisView::ProcessorAutomationNode* pan)
2080 bool showit = pan->menu_item->get_active();
2081 bool redraw = false;
2083 if (pan->view == 0 && showit) {
2084 add_processor_automation_curve (rai->processor, pan->what);
2088 if (pan->view && pan->view->set_marked_for_display (showit)) {
2092 if (redraw && !no_redraw) {
2098 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2100 if (c.type == RouteProcessorChange::MeterPointChange) {
2101 /* nothing to do if only the meter point has changed */
2105 using namespace Menu_Helpers;
2107 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2108 (*i)->valid = false;
2111 setup_processor_menu_and_curves ();
2113 bool deleted_processor_automation = false;
2115 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2117 list<ProcessorAutomationInfo*>::iterator tmp;
2125 processor_automation.erase (i);
2126 deleted_processor_automation = true;
2133 if (deleted_processor_automation && !no_redraw) {
2138 boost::shared_ptr<AutomationLine>
2139 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2141 ProcessorAutomationNode* pan;
2143 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2149 return boost::shared_ptr<AutomationLine>();
2153 RouteTimeAxisView::reset_processor_automation_curves ()
2155 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2161 RouteTimeAxisView::can_edit_name () const
2163 /* we do not allow track name changes if it is record enabled
2165 return !_route->record_enabled();
2169 RouteTimeAxisView::update_rec_display ()
2171 RouteUI::update_rec_display ();
2175 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2177 if (_ignore_set_layer_display) {
2181 if (apply_to_selection) {
2182 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2186 _view->set_layer_display (d);
2189 set_gui_property (X_("layer-display"), enum_2_string (d));
2194 RouteTimeAxisView::layer_display () const
2197 return _view->layer_display ();
2200 /* we don't know, since we don't have a _view, so just return something */
2206 boost::shared_ptr<AutomationTimeAxisView>
2207 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2209 AutomationTracks::iterator i = _automation_tracks.find(param);
2210 if (i != _automation_tracks.end()) {
2213 return boost::shared_ptr<AutomationTimeAxisView>();
2218 RouteTimeAxisView::fast_update ()
2220 gm.get_level_meter().update_meters ();
2224 RouteTimeAxisView::hide_meter ()
2227 gm.get_level_meter().hide_meters ();
2231 RouteTimeAxisView::show_meter ()
2237 RouteTimeAxisView::reset_meter ()
2239 if (Config->get_show_track_meters()) {
2240 int meter_width = 3;
2241 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2244 gm.get_level_meter().setup_meters (height - 9, meter_width);
2251 RouteTimeAxisView::clear_meter ()
2253 gm.get_level_meter().clear_meters ();
2257 RouteTimeAxisView::meter_changed ()
2259 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2261 if (_route && !no_redraw) {
2264 // reset peak when meter point changes
2265 gm.reset_peak_display();
2269 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2272 if (_route && !no_redraw) {
2278 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2280 using namespace Menu_Helpers;
2282 if (!_underlay_streams.empty()) {
2283 MenuList& parent_items = parent_menu->items();
2284 Menu* gs_menu = manage (new Menu);
2285 gs_menu->set_name ("ArdourContextMenu");
2286 MenuList& gs_items = gs_menu->items();
2288 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2290 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2291 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2292 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2298 RouteTimeAxisView::set_underlay_state()
2300 if (!underlay_xml_node) {
2304 XMLNodeList nlist = underlay_xml_node->children();
2305 XMLNodeConstIterator niter;
2306 XMLNode *child_node;
2308 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2309 child_node = *niter;
2311 if (child_node->name() != "Underlay") {
2315 XMLProperty* prop = child_node->property ("id");
2317 PBD::ID id (prop->value());
2319 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2322 add_underlay(v->view(), false);
2331 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2337 RouteTimeAxisView& other = v->trackview();
2339 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2340 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2341 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2345 _underlay_streams.push_back(v);
2346 other._underlay_mirrors.push_back(this);
2348 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2350 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2352 if (!underlay_xml_node) {
2353 underlay_xml_node = xml_node->add_child("Underlays");
2356 XMLNode* node = underlay_xml_node->add_child("Underlay");
2357 XMLProperty* prop = node->add_property("id");
2358 prop->set_value(v->trackview().route()->id().to_s());
2365 RouteTimeAxisView::remove_underlay (StreamView* v)
2371 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2372 RouteTimeAxisView& other = v->trackview();
2374 if (it != _underlay_streams.end()) {
2375 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2377 if (gm == other._underlay_mirrors.end()) {
2378 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2382 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2384 _underlay_streams.erase(it);
2385 other._underlay_mirrors.erase(gm);
2387 if (underlay_xml_node) {
2388 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2394 RouteTimeAxisView::set_button_names ()
2396 if (_route && _route->solo_safe()) {
2397 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2399 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2401 if (Config->get_solo_control_is_listen_control()) {
2402 switch (Config->get_listen_position()) {
2403 case AfterFaderListen:
2404 solo_button->set_text (_("A"));
2405 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2407 case PreFaderListen:
2408 solo_button->set_text (_("P"));
2409 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2413 solo_button->set_text (_("s"));
2414 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2416 mute_button->set_text (_("m"));
2420 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2422 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2423 if (i != _main_automation_menu_map.end()) {
2427 i = _subplugin_menu_map.find (param);
2428 if (i != _subplugin_menu_map.end()) {
2436 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2438 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2440 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2444 gain_track.reset (new AutomationTimeAxisView (_session,
2445 _route, _route->amp(), c, param,
2450 _route->amp()->describe_parameter(param)));
2453 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2456 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2460 void add_region_to_list (RegionView* rv, RegionList* l)
2462 l->push_back (rv->region());
2466 RouteTimeAxisView::combine_regions ()
2468 /* as of may 2011, we do not offer uncombine for MIDI tracks
2471 if (!is_audio_track()) {
2479 RegionList selected_regions;
2480 boost::shared_ptr<Playlist> playlist = track()->playlist();
2482 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2484 if (selected_regions.size() < 2) {
2488 playlist->clear_changes ();
2489 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2491 _session->add_command (new StatefulDiffCommand (playlist));
2492 /* make the new region be selected */
2494 return _view->find_view (compound_region);
2498 RouteTimeAxisView::uncombine_regions ()
2500 /* as of may 2011, we do not offer uncombine for MIDI tracks
2502 if (!is_audio_track()) {
2510 RegionList selected_regions;
2511 boost::shared_ptr<Playlist> playlist = track()->playlist();
2513 /* have to grab selected regions first because the uncombine is going
2514 * to change that in the middle of the list traverse
2517 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2519 playlist->clear_changes ();
2521 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2522 playlist->uncombine (*i);
2525 _session->add_command (new StatefulDiffCommand (playlist));
2529 RouteTimeAxisView::state_id() const
2531 return string_compose ("rtav %1", _route->id().to_s());
2536 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2538 TimeAxisView::remove_child (c);
2540 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2542 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2543 if (i->second == a) {
2544 _automation_tracks.erase (i);