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->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
715 items.push_back (SeparatorElem());
716 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
717 if (!Profile->get_sae()) {
718 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
720 items.push_front (SeparatorElem());
721 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
726 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
728 if (apply_to_selection) {
729 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
732 bool needs_bounce = false;
734 if (!track()->can_use_mode (mode, needs_bounce)) {
740 cerr << "would bounce this one\n";
745 track()->set_mode (mode);
747 rec_enable_button->remove ();
750 case ARDOUR::NonLayered:
752 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
753 rec_enable_button->set_text (string());
755 case ARDOUR::Destructive:
756 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
757 rec_enable_button->set_text (string());
761 rec_enable_button->show_all ();
766 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
768 TimeAxisView::show_timestretch (start, end, layers, layer);
778 /* check that the time selection was made in our route, or our route group.
779 remember that route_group() == 0 implies the route is *not* in a edit group.
782 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
783 /* this doesn't apply to us */
787 /* ignore it if our edit group is not active */
789 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
794 if (timestretch_rect == 0) {
795 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
796 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
797 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
800 timestretch_rect->show ();
801 timestretch_rect->raise_to_top ();
803 double const x1 = start / _editor.get_current_zoom();
804 double const x2 = (end - 1) / _editor.get_current_zoom();
806 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
807 x2, current_height() * (layers - layer) / layers));
811 RouteTimeAxisView::hide_timestretch ()
813 TimeAxisView::hide_timestretch ();
815 if (timestretch_rect) {
816 timestretch_rect->hide ();
821 RouteTimeAxisView::show_selection (TimeSelection& ts)
825 /* ignore it if our edit group is not active or if the selection was started
826 in some other track or route group (remember that route_group() == 0 means
827 that the track is not in an route group).
830 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
831 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
837 TimeAxisView::show_selection (ts);
841 RouteTimeAxisView::set_height (uint32_t h)
844 bool height_changed = (height == 0) || (h != height);
847 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
850 gm.get_level_meter().setup_meters (gmlen, meter_width);
852 TimeAxisView::set_height (h);
855 _view->set_height ((double) current_height());
858 if (height >= preset_height (HeightNormal)) {
862 gm.get_gain_slider().show();
864 if (!_route || _route->is_monitor()) {
869 if (rec_enable_button)
870 rec_enable_button->show();
872 route_group_button.show();
873 automation_button.show();
875 if (is_track() && track()->mode() == ARDOUR::Normal) {
876 playlist_button.show();
883 gm.get_gain_slider().hide();
885 if (!_route || _route->is_monitor()) {
890 if (rec_enable_button)
891 rec_enable_button->show();
893 route_group_button.hide ();
894 automation_button.hide ();
896 if (is_track() && track()->mode() == ARDOUR::Normal) {
897 playlist_button.hide ();
902 if (height_changed && !no_redraw) {
903 /* only emit the signal if the height really changed */
909 RouteTimeAxisView::route_color_changed ()
912 _view->apply_color (color(), StreamView::RegionColor);
917 RouteTimeAxisView::reset_samples_per_pixel ()
919 set_samples_per_pixel (_editor.get_current_zoom());
923 RouteTimeAxisView::set_samples_per_pixel (double fpp)
928 speed = track()->speed();
932 _view->set_samples_per_pixel (fpp * speed);
935 TimeAxisView::set_samples_per_pixel (fpp * speed);
939 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
941 if (!mitem->get_active()) {
942 /* this is one of the two calls made when these radio menu items change status. this one
943 is for the item that became inactive, and we want to ignore it.
948 if (apply_to_selection) {
949 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
952 track()->set_align_choice (choice);
958 RouteTimeAxisView::rename_current_playlist ()
960 ArdourPrompter prompter (true);
963 boost::shared_ptr<Track> tr = track();
964 if (!tr || tr->destructive()) {
968 boost::shared_ptr<Playlist> pl = tr->playlist();
973 prompter.set_title (_("Rename Playlist"));
974 prompter.set_prompt (_("New name for playlist:"));
975 prompter.set_initial_text (pl->name());
976 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
977 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
979 switch (prompter.run ()) {
980 case Gtk::RESPONSE_ACCEPT:
981 prompter.get_result (name);
993 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
995 std::string ret (basename);
997 std::string const group_string = "." + route_group()->name() + ".";
999 // iterate through all playlists
1001 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1002 std::string tmp = (*i)->name();
1004 std::string::size_type idx = tmp.find(group_string);
1005 // find those which belong to this group
1006 if (idx != string::npos) {
1007 tmp = tmp.substr(idx + group_string.length());
1009 // and find the largest current number
1011 if (x > maxnumber) {
1020 snprintf (buf, sizeof(buf), "%d", maxnumber);
1022 ret = this->name() + "." + route_group()->name () + "." + buf;
1028 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1032 boost::shared_ptr<Track> tr = track ();
1033 if (!tr || tr->destructive()) {
1037 boost::shared_ptr<const Playlist> pl = tr->playlist();
1044 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1045 name = resolve_new_group_playlist_name(name, playlists_before_op);
1048 while (_session->playlists->by_name(name)) {
1049 name = Playlist::bump_name (name, *_session);
1052 // TODO: The prompter "new" button should be de-activated if the user
1053 // specifies a playlist name which already exists in the session.
1057 ArdourPrompter prompter (true);
1059 prompter.set_title (_("New Copy Playlist"));
1060 prompter.set_prompt (_("Name for new playlist:"));
1061 prompter.set_initial_text (name);
1062 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1063 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1064 prompter.show_all ();
1066 switch (prompter.run ()) {
1067 case Gtk::RESPONSE_ACCEPT:
1068 prompter.get_result (name);
1076 if (name.length()) {
1077 tr->use_copy_playlist ();
1078 tr->playlist()->set_name (name);
1083 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1087 boost::shared_ptr<Track> tr = track ();
1088 if (!tr || tr->destructive()) {
1092 boost::shared_ptr<const Playlist> pl = tr->playlist();
1099 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1100 name = resolve_new_group_playlist_name(name,playlists_before_op);
1103 while (_session->playlists->by_name(name)) {
1104 name = Playlist::bump_name (name, *_session);
1110 ArdourPrompter prompter (true);
1112 prompter.set_title (_("New Playlist"));
1113 prompter.set_prompt (_("Name for new playlist:"));
1114 prompter.set_initial_text (name);
1115 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1116 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1118 switch (prompter.run ()) {
1119 case Gtk::RESPONSE_ACCEPT:
1120 prompter.get_result (name);
1128 if (name.length()) {
1129 tr->use_new_playlist ();
1130 tr->playlist()->set_name (name);
1135 RouteTimeAxisView::clear_playlist ()
1137 boost::shared_ptr<Track> tr = track ();
1138 if (!tr || tr->destructive()) {
1142 boost::shared_ptr<Playlist> pl = tr->playlist();
1147 _editor.clear_playlist (pl);
1151 RouteTimeAxisView::speed_changed ()
1153 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1157 RouteTimeAxisView::update_diskstream_display ()
1167 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1169 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1171 /* special case: select/deselect all tracks */
1172 if (_editor.get_selection().selected (this)) {
1173 _editor.get_selection().clear_tracks ();
1175 _editor.select_all_tracks ();
1181 switch (ArdourKeyboard::selection_type (ev->state)) {
1182 case Selection::Toggle:
1183 _editor.get_selection().toggle (this);
1186 case Selection::Set:
1187 _editor.get_selection().set (this);
1190 case Selection::Extend:
1191 _editor.extend_selection_to_track (*this);
1194 case Selection::Add:
1195 _editor.get_selection().add (this);
1201 RouteTimeAxisView::set_selected_points (PointSelection& points)
1203 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1204 (*i)->set_selected_points (points);
1209 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1212 _view->set_selected_regionviews (regions);
1216 /** Add the selectable things that we have to a list.
1217 * @param results List to add things to.
1220 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1225 speed = track()->speed();
1228 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1229 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1231 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1232 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1235 /* pick up visible automation tracks */
1237 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1238 if (!(*i)->hidden()) {
1239 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1245 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1248 _view->get_inverted_selectables (sel, results);
1251 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1252 if (!(*i)->hidden()) {
1253 (*i)->get_inverted_selectables (sel, results);
1261 RouteTimeAxisView::route_group () const
1263 return _route->route_group();
1267 RouteTimeAxisView::name() const
1269 return _route->name();
1272 boost::shared_ptr<Playlist>
1273 RouteTimeAxisView::playlist () const
1275 boost::shared_ptr<Track> tr;
1277 if ((tr = track()) != 0) {
1278 return tr->playlist();
1280 return boost::shared_ptr<Playlist> ();
1285 RouteTimeAxisView::name_entry_changed ()
1287 TimeAxisView::name_entry_changed ();
1289 string x = name_entry->get_text ();
1291 if (x == _route->name()) {
1295 strip_whitespace_edges (x);
1297 if (x.length() == 0) {
1298 name_entry->set_text (_route->name());
1302 if (_session->route_name_internal (x)) {
1303 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1305 name_entry->grab_focus ();
1306 } else if (RouteUI::verify_new_route_name (x)) {
1307 _route->set_name (x);
1309 name_entry->grab_focus ();
1313 boost::shared_ptr<Region>
1314 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1316 boost::shared_ptr<Playlist> pl = playlist ();
1319 return pl->find_next_region (pos, point, dir);
1322 return boost::shared_ptr<Region> ();
1326 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1328 boost::shared_ptr<Playlist> pl = playlist ();
1331 return pl->find_next_region_boundary (pos, dir);
1338 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1340 boost::shared_ptr<Playlist> what_we_got;
1341 boost::shared_ptr<Track> tr = track ();
1342 boost::shared_ptr<Playlist> playlist;
1345 /* route is a bus, not a track */
1349 playlist = tr->playlist();
1351 TimeSelection time (selection.time);
1352 float const speed = tr->speed();
1353 if (speed != 1.0f) {
1354 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1355 (*i).start = session_frame_to_track_frame((*i).start, speed);
1356 (*i).end = session_frame_to_track_frame((*i).end, speed);
1360 playlist->clear_changes ();
1361 playlist->clear_owned_changes ();
1365 if (playlist->cut (time) != 0) {
1366 if (Config->get_edit_mode() == Ripple)
1367 playlist->ripple(time.start(), -time.length(), NULL);
1368 // no need to exclude any regions from rippling here
1370 vector<Command*> cmds;
1371 playlist->rdiff (cmds);
1372 _session->add_commands (cmds);
1374 _session->add_command (new StatefulDiffCommand (playlist));
1379 if ((what_we_got = playlist->cut (time)) != 0) {
1380 _editor.get_cut_buffer().add (what_we_got);
1381 if (Config->get_edit_mode() == Ripple)
1382 playlist->ripple(time.start(), -time.length(), NULL);
1383 // no need to exclude any regions from rippling here
1385 vector<Command*> cmds;
1386 playlist->rdiff (cmds);
1387 _session->add_commands (cmds);
1389 _session->add_command (new StatefulDiffCommand (playlist));
1393 if ((what_we_got = playlist->copy (time)) != 0) {
1394 _editor.get_cut_buffer().add (what_we_got);
1399 if ((what_we_got = playlist->cut (time)) != 0) {
1400 if (Config->get_edit_mode() == Ripple)
1401 playlist->ripple(time.start(), -time.length(), NULL);
1402 // no need to exclude any regions from rippling here
1404 vector<Command*> cmds;
1405 playlist->rdiff (cmds);
1406 _session->add_commands (cmds);
1407 _session->add_command (new StatefulDiffCommand (playlist));
1408 what_we_got->release ();
1415 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1421 boost::shared_ptr<Playlist> pl = playlist ();
1422 PlaylistSelection::iterator p;
1424 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1426 if (p == selection.playlists.end()) {
1430 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1432 if (track()->speed() != 1.0f) {
1433 pos = session_frame_to_track_frame (pos, track()->speed());
1434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1437 pl->clear_changes ();
1438 if (Config->get_edit_mode() == Ripple) {
1439 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1440 framecnt_t amount = extent.second - extent.first;
1441 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1443 pl->paste (*p, pos, times);
1445 vector<Command*> cmds;
1447 _session->add_commands (cmds);
1449 _session->add_command (new StatefulDiffCommand (pl));
1455 struct PlaylistSorter {
1456 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1457 return a->sort_id() < b->sort_id();
1462 RouteTimeAxisView::build_playlist_menu ()
1464 using namespace Menu_Helpers;
1470 delete playlist_action_menu;
1471 playlist_action_menu = new Menu;
1472 playlist_action_menu->set_name ("ArdourContextMenu");
1474 MenuList& playlist_items = playlist_action_menu->items();
1475 playlist_action_menu->set_name ("ArdourContextMenu");
1476 playlist_items.clear();
1478 RadioMenuItem::Group playlist_group;
1479 boost::shared_ptr<Track> tr = track ();
1481 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1483 /* sort the playlists */
1485 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1487 /* add the playlists to the menu */
1488 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1489 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1490 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1491 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1493 if (tr->playlist()->id() == (*i)->id()) {
1499 playlist_items.push_back (SeparatorElem());
1500 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1501 playlist_items.push_back (SeparatorElem());
1503 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1504 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1505 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1508 // Use a label which tells the user what is happening
1509 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1510 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1514 playlist_items.push_back (SeparatorElem());
1515 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1516 playlist_items.push_back (SeparatorElem());
1518 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1522 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1524 assert (is_track());
1526 // exit if we were triggered by deactivating the old playlist
1527 if (!item->get_active()) {
1531 boost::shared_ptr<Playlist> pl (wpl.lock());
1537 if (track()->playlist() == pl) {
1538 // exit when use_playlist is called by the creation of the playlist menu
1539 // or the playlist choice is unchanged
1543 track()->use_playlist (pl);
1545 RouteGroup* rg = route_group();
1547 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1548 std::string group_string = "." + rg->name() + ".";
1550 std::string take_name = pl->name();
1551 std::string::size_type idx = take_name.find(group_string);
1553 if (idx == std::string::npos)
1556 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1558 boost::shared_ptr<RouteList> rl (rg->route_list());
1560 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1561 if ((*i) == this->route()) {
1565 std::string playlist_name = (*i)->name()+group_string+take_name;
1567 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1572 if (track->freeze_state() == Track::Frozen) {
1573 /* Don't change playlists of frozen tracks */
1577 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1579 // No playlist for this track for this take yet, make it
1580 track->use_new_playlist();
1581 track->playlist()->set_name(playlist_name);
1583 track->use_playlist(ipl);
1590 RouteTimeAxisView::update_playlist_tip ()
1592 RouteGroup* rg = route_group ();
1593 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1594 string group_string = "." + rg->name() + ".";
1596 string take_name = track()->playlist()->name();
1597 string::size_type idx = take_name.find(group_string);
1599 if (idx != string::npos) {
1600 /* find the bit containing the take number / name */
1601 take_name = take_name.substr (idx + group_string.length());
1603 /* set the playlist button tooltip to the take name */
1604 ARDOUR_UI::instance()->set_tip (
1606 string_compose(_("Take: %1.%2"),
1607 Glib::Markup::escape_text(rg->name()),
1608 Glib::Markup::escape_text(take_name))
1615 /* set the playlist button tooltip to the playlist name */
1616 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1621 RouteTimeAxisView::show_playlist_selector ()
1623 _editor.playlist_selector().show_for (this);
1627 RouteTimeAxisView::map_frozen ()
1633 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1635 switch (track()->freeze_state()) {
1637 playlist_button.set_sensitive (false);
1638 rec_enable_button->set_sensitive (false);
1641 playlist_button.set_sensitive (true);
1642 rec_enable_button->set_sensitive (true);
1648 RouteTimeAxisView::color_handler ()
1650 //case cTimeStretchOutline:
1651 if (timestretch_rect) {
1652 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1654 //case cTimeStretchFill:
1655 if (timestretch_rect) {
1656 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1662 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1663 * Will add track if necessary.
1666 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1668 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1669 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1672 /* it doesn't exist yet, so we don't care about the button state: just add it */
1673 create_automation_child (param, true);
1676 bool yn = menu->get_active();
1677 bool changed = false;
1679 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1681 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1682 will have done that for us.
1685 if (changed && !no_redraw) {
1693 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1695 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1701 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1703 if (menu && !_hidden) {
1704 ignore_toggle = true;
1705 menu->set_active (false);
1706 ignore_toggle = false;
1709 if (_route && !no_redraw) {
1716 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1718 if (apply_to_selection) {
1719 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1723 /* Show our automation */
1725 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1726 i->second->set_marked_for_display (true);
1728 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1731 menu->set_active(true);
1736 /* Show processor automation */
1738 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1739 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1740 if ((*ii)->view == 0) {
1741 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1744 (*ii)->menu_item->set_active (true);
1757 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1759 if (apply_to_selection) {
1760 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1764 /* Show our automation */
1766 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1767 if (i->second->has_automation()) {
1768 i->second->set_marked_for_display (true);
1770 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1772 menu->set_active(true);
1777 /* Show processor automation */
1779 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1780 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1781 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1782 (*ii)->menu_item->set_active (true);
1794 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1796 if (apply_to_selection) {
1797 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1801 /* Hide our automation */
1803 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1804 i->second->set_marked_for_display (false);
1806 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1809 menu->set_active (false);
1813 /* Hide processor automation */
1815 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1816 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1817 (*ii)->menu_item->set_active (false);
1828 RouteTimeAxisView::region_view_added (RegionView* rv)
1830 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1831 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1832 boost::shared_ptr<AutomationTimeAxisView> atv;
1834 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1839 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1840 (*i)->add_ghost(rv);
1844 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1846 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1852 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1854 parent.remove_processor_automation_node (this);
1858 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1861 remove_child (pan->view);
1865 RouteTimeAxisView::ProcessorAutomationNode*
1866 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1868 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1870 if ((*i)->processor == processor) {
1872 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1873 if ((*ii)->what == what) {
1883 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1885 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1888 ProcessorAutomationNode* pan;
1890 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1891 /* session state may never have been saved with new plugin */
1892 error << _("programming error: ")
1893 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1894 processor->name(), what.type(), (int) what.channel(), what.id() )
1904 boost::shared_ptr<AutomationControl> control
1905 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1907 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1908 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1909 _editor, *this, false, parent_canvas,
1910 processor->describe_parameter (what), processor->name()));
1912 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1914 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1917 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1922 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1925 pan->menu_item->set_active (false);
1934 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1936 boost::shared_ptr<Processor> processor (p.lock ());
1938 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1939 /* The Amp processor is a special case and is dealt with separately */
1943 set<Evoral::Parameter> existing;
1945 processor->what_has_data (existing);
1947 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1949 Evoral::Parameter param (*i);
1950 boost::shared_ptr<AutomationLine> al;
1952 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1955 add_processor_automation_curve (processor, param);
1961 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1963 using namespace Menu_Helpers;
1967 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1969 _automation_tracks[param] = track;
1971 /* existing state overrides "show" argument */
1972 string s = track->gui_property ("visible");
1974 show = string_is_affirmative (s);
1977 /* this might or might not change the visibility status, so don't rely on it */
1978 track->set_marked_for_display (show);
1980 if (show && !no_redraw) {
1984 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1985 /* MIDI-related parameters are always in the menu, there's no
1986 reason to rebuild the menu just because we added a automation
1987 lane for one of them. But if we add a non-MIDI automation
1988 lane, then we need to invalidate the display menu.
1990 delete display_menu;
1996 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1998 boost::shared_ptr<Processor> processor (p.lock ());
2000 if (!processor || !processor->display_to_user ()) {
2004 /* we use this override to veto the Amp processor from the plugin menu,
2005 as its automation lane can be accessed using the special "Fader" menu
2009 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2013 using namespace Menu_Helpers;
2014 ProcessorAutomationInfo *rai;
2015 list<ProcessorAutomationInfo*>::iterator x;
2017 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2019 if (automatable.empty()) {
2023 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2024 if ((*x)->processor == processor) {
2029 if (x == processor_automation.end()) {
2031 rai = new ProcessorAutomationInfo (processor);
2032 processor_automation.push_back (rai);
2040 /* any older menu was deleted at the top of processors_changed()
2041 when we cleared the subplugin menu.
2044 rai->menu = manage (new Menu);
2045 MenuList& items = rai->menu->items();
2046 rai->menu->set_name ("ArdourContextMenu");
2050 std::set<Evoral::Parameter> has_visible_automation;
2051 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2053 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2055 ProcessorAutomationNode* pan;
2056 Gtk::CheckMenuItem* mitem;
2058 string name = processor->describe_parameter (*i);
2060 items.push_back (CheckMenuElem (name));
2061 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2063 _subplugin_menu_map[*i] = mitem;
2065 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2066 mitem->set_active(true);
2069 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2073 pan = new ProcessorAutomationNode (*i, mitem, *this);
2075 rai->lines.push_back (pan);
2079 pan->menu_item = mitem;
2083 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2086 /* add the menu for this processor, because the subplugin
2087 menu is always cleared at the top of processors_changed().
2088 this is the result of some poor design in gtkmm and/or
2092 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2097 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2098 RouteTimeAxisView::ProcessorAutomationNode* pan)
2100 bool showit = pan->menu_item->get_active();
2101 bool redraw = false;
2103 if (pan->view == 0 && showit) {
2104 add_processor_automation_curve (rai->processor, pan->what);
2108 if (pan->view && pan->view->set_marked_for_display (showit)) {
2112 if (redraw && !no_redraw) {
2118 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2120 if (c.type == RouteProcessorChange::MeterPointChange) {
2121 /* nothing to do if only the meter point has changed */
2125 using namespace Menu_Helpers;
2127 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2128 (*i)->valid = false;
2131 setup_processor_menu_and_curves ();
2133 bool deleted_processor_automation = false;
2135 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2137 list<ProcessorAutomationInfo*>::iterator tmp;
2145 processor_automation.erase (i);
2146 deleted_processor_automation = true;
2153 if (deleted_processor_automation && !no_redraw) {
2158 boost::shared_ptr<AutomationLine>
2159 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2161 ProcessorAutomationNode* pan;
2163 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2169 return boost::shared_ptr<AutomationLine>();
2173 RouteTimeAxisView::reset_processor_automation_curves ()
2175 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2181 RouteTimeAxisView::can_edit_name () const
2183 /* we do not allow track name changes if it is record enabled
2185 return !_route->record_enabled();
2189 RouteTimeAxisView::update_rec_display ()
2191 RouteUI::update_rec_display ();
2195 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2197 if (_ignore_set_layer_display) {
2201 if (apply_to_selection) {
2202 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2206 _view->set_layer_display (d);
2209 set_gui_property (X_("layer-display"), enum_2_string (d));
2214 RouteTimeAxisView::layer_display () const
2217 return _view->layer_display ();
2220 /* we don't know, since we don't have a _view, so just return something */
2226 boost::shared_ptr<AutomationTimeAxisView>
2227 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2229 AutomationTracks::iterator i = _automation_tracks.find(param);
2230 if (i != _automation_tracks.end()) {
2233 return boost::shared_ptr<AutomationTimeAxisView>();
2238 RouteTimeAxisView::fast_update ()
2240 gm.get_level_meter().update_meters ();
2244 RouteTimeAxisView::hide_meter ()
2247 gm.get_level_meter().hide_meters ();
2251 RouteTimeAxisView::show_meter ()
2257 RouteTimeAxisView::reset_meter ()
2259 if (Config->get_show_track_meters()) {
2260 int meter_width = 3;
2261 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2264 gm.get_level_meter().setup_meters (height - 9, meter_width);
2271 RouteTimeAxisView::clear_meter ()
2273 gm.get_level_meter().clear_meters ();
2277 RouteTimeAxisView::meter_changed ()
2279 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2281 if (_route && !no_redraw) {
2284 // reset peak when meter point changes
2285 gm.reset_peak_display();
2289 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2292 if (_route && !no_redraw) {
2298 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2300 using namespace Menu_Helpers;
2302 if (!_underlay_streams.empty()) {
2303 MenuList& parent_items = parent_menu->items();
2304 Menu* gs_menu = manage (new Menu);
2305 gs_menu->set_name ("ArdourContextMenu");
2306 MenuList& gs_items = gs_menu->items();
2308 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2310 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2311 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2312 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2318 RouteTimeAxisView::set_underlay_state()
2320 if (!underlay_xml_node) {
2324 XMLNodeList nlist = underlay_xml_node->children();
2325 XMLNodeConstIterator niter;
2326 XMLNode *child_node;
2328 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2329 child_node = *niter;
2331 if (child_node->name() != "Underlay") {
2335 XMLProperty* prop = child_node->property ("id");
2337 PBD::ID id (prop->value());
2339 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2342 add_underlay(v->view(), false);
2351 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2357 RouteTimeAxisView& other = v->trackview();
2359 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2360 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2361 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2365 _underlay_streams.push_back(v);
2366 other._underlay_mirrors.push_back(this);
2368 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2370 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2372 if (!underlay_xml_node) {
2373 underlay_xml_node = xml_node->add_child("Underlays");
2376 XMLNode* node = underlay_xml_node->add_child("Underlay");
2377 XMLProperty* prop = node->add_property("id");
2378 prop->set_value(v->trackview().route()->id().to_s());
2385 RouteTimeAxisView::remove_underlay (StreamView* v)
2391 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2392 RouteTimeAxisView& other = v->trackview();
2394 if (it != _underlay_streams.end()) {
2395 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2397 if (gm == other._underlay_mirrors.end()) {
2398 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2402 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2404 _underlay_streams.erase(it);
2405 other._underlay_mirrors.erase(gm);
2407 if (underlay_xml_node) {
2408 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2414 RouteTimeAxisView::set_button_names ()
2416 if (_route && _route->solo_safe()) {
2417 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2419 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2421 if (Config->get_solo_control_is_listen_control()) {
2422 switch (Config->get_listen_position()) {
2423 case AfterFaderListen:
2424 solo_button->set_text (_("A"));
2425 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2427 case PreFaderListen:
2428 solo_button->set_text (_("P"));
2429 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2433 solo_button->set_text (_("s"));
2434 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2436 mute_button->set_text (_("m"));
2440 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2442 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2443 if (i != _main_automation_menu_map.end()) {
2447 i = _subplugin_menu_map.find (param);
2448 if (i != _subplugin_menu_map.end()) {
2456 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2458 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2460 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2464 gain_track.reset (new AutomationTimeAxisView (_session,
2465 _route, _route->amp(), c, param,
2470 _route->amp()->describe_parameter(param)));
2473 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2476 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2480 void add_region_to_list (RegionView* rv, RegionList* l)
2482 l->push_back (rv->region());
2486 RouteTimeAxisView::combine_regions ()
2488 /* as of may 2011, we do not offer uncombine for MIDI tracks
2491 if (!is_audio_track()) {
2499 RegionList selected_regions;
2500 boost::shared_ptr<Playlist> playlist = track()->playlist();
2502 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2504 if (selected_regions.size() < 2) {
2508 playlist->clear_changes ();
2509 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2511 _session->add_command (new StatefulDiffCommand (playlist));
2512 /* make the new region be selected */
2514 return _view->find_view (compound_region);
2518 RouteTimeAxisView::uncombine_regions ()
2520 /* as of may 2011, we do not offer uncombine for MIDI tracks
2522 if (!is_audio_track()) {
2530 RegionList selected_regions;
2531 boost::shared_ptr<Playlist> playlist = track()->playlist();
2533 /* have to grab selected regions first because the uncombine is going
2534 * to change that in the middle of the list traverse
2537 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2539 playlist->clear_changes ();
2541 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2542 playlist->uncombine (*i);
2545 _session->add_command (new StatefulDiffCommand (playlist));
2549 RouteTimeAxisView::state_id() const
2551 return string_compose ("rtav %1", _route->id().to_s());
2556 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2558 TimeAxisView::remove_child (c);
2560 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2562 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2563 if (i->second == a) {
2564 _automation_tracks.erase (i);