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/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
89 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider_desensitised;
92 RouteTimeAxisView::setup_slider_pix ()
94 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
95 throw failed_constructor ();
98 if ((slider_desensitised = ::get_icon ("fader_belt_h_desensitised")) == 0) {
99 throw failed_constructor ();
103 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
106 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
108 , parent_canvas (canvas)
110 , button_table (3, 3)
111 , route_group_button (_("g"))
112 , playlist_button (_("p"))
113 , automation_button (_("a"))
114 , automation_action_menu (0)
115 , plugins_submenu_item (0)
116 , route_group_menu (0)
117 , playlist_action_menu (0)
119 , color_mode_menu (0)
120 , gm (sess, slider, slider_desensitised, true, 115)
125 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
127 RouteUI::set_route (rt);
129 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
130 gm.get_level_meter().set_no_show_all();
131 gm.get_level_meter().setup_meters(50);
132 gm.update_gain_sensitive ();
134 string str = gui_property ("height");
136 set_height (atoi (str));
138 set_height (preset_height (HeightNormal));
141 if (!_route->is_hidden()) {
142 if (gui_property ("visible").empty()) {
143 set_gui_property ("visible", true);
146 set_gui_property ("visible", false);
150 update_solo_display ();
152 timestretch_rect = 0;
155 ignore_toggle = false;
157 route_group_button.set_name ("route button");
158 playlist_button.set_name ("route button");
159 automation_button.set_name ("route button");
161 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
162 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
163 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
169 switch (track()->mode()) {
171 case ARDOUR::NonLayered:
172 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
174 case ARDOUR::Destructive:
175 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
179 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
181 if (is_midi_track()) {
182 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
187 rec_enable_button->set_sensitive (_session->writable());
190 controls_hbox.pack_start(gm.get_level_meter(), false, false);
191 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
192 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
193 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
195 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
197 if (!_route->is_master()) {
198 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
201 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
202 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
204 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
205 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
206 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
207 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
209 if (is_midi_track()) {
210 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
212 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
217 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 if (is_track() && track()->mode() == ARDOUR::Normal) {
220 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
225 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
226 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
230 str = gui_property ("layer-display");
232 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
235 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
236 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
238 /* pick up the correct freeze state */
242 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
243 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
244 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
246 PropertyList* plist = new PropertyList();
248 plist->add (ARDOUR::Properties::edit, true);
249 plist->add (ARDOUR::Properties::mute, true);
250 plist->add (ARDOUR::Properties::solo, true);
252 route_group_menu = new RouteGroupMenu (_session, plist);
254 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
255 gm.get_gain_slider().set_name ("TrackGainFader");
257 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
263 RouteTimeAxisView::~RouteTimeAxisView ()
265 CatchDeletion (this);
267 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
271 delete playlist_action_menu;
272 playlist_action_menu = 0;
277 _automation_tracks.clear ();
279 delete route_group_menu;
283 RouteTimeAxisView::post_construct ()
285 /* map current state of the route */
287 update_diskstream_display ();
288 setup_processor_menu_and_curves ();
289 reset_processor_automation_curves ();
292 /** Set up the processor menu for the current set of processors, and
293 * display automation curves for any parameters which have data.
296 RouteTimeAxisView::setup_processor_menu_and_curves ()
298 _subplugin_menu_map.clear ();
299 subplugin_menu.items().clear ();
300 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
301 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
305 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
307 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
308 if (_route->route_group()) {
309 _route->route_group()->remove (_route);
315 r.push_back (route ());
317 route_group_menu->build (r);
318 route_group_menu->menu()->popup (ev->button, ev->time);
324 RouteTimeAxisView::playlist_changed ()
330 RouteTimeAxisView::label_view ()
332 string x = _route->name();
334 if (x != name_entry.get_text()) {
335 name_entry.set_text (x);
338 if (x != name_label.get_text()) {
339 name_label.set_text (x);
342 ARDOUR_UI::instance()->set_tip (name_entry, x);
346 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
348 if (what_changed.contains (ARDOUR::Properties::name)) {
354 RouteTimeAxisView::take_name_changed (void *src)
362 RouteTimeAxisView::playlist_click ()
364 build_playlist_menu ();
365 conditionally_add_to_selection ();
366 playlist_action_menu->popup (1, gtk_get_current_event_time());
370 RouteTimeAxisView::automation_click ()
372 conditionally_add_to_selection ();
373 build_automation_action_menu (false);
374 automation_action_menu->popup (1, gtk_get_current_event_time());
378 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
380 using namespace Menu_Helpers;
382 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
383 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
386 detach_menu (subplugin_menu);
388 _main_automation_menu_map.clear ();
389 delete automation_action_menu;
390 automation_action_menu = new Menu;
392 MenuList& items = automation_action_menu->items();
394 automation_action_menu->set_name ("ArdourContextMenu");
396 items.push_back (MenuElem (_("Show All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
399 items.push_back (MenuElem (_("Show Existing Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
402 items.push_back (MenuElem (_("Hide All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
405 items.push_back (SeparatorElem ());
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above */
410 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
411 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
415 RouteTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers;
421 TimeAxisView::build_display_menu ();
423 /* now fill it with our stuff */
425 MenuList& items = display_menu->items();
426 display_menu->set_name ("ArdourContextMenu");
428 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
431 detach_menu (*_size_menu);
434 items.push_back (MenuElem (_("Height"), *_size_menu));
436 items.push_back (SeparatorElem());
438 if (!Profile->get_sae()) {
439 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
440 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
441 items.push_back (SeparatorElem());
444 // Hook for derived classes to add type specific stuff
445 append_extra_display_menu_items ();
449 Menu* layers_menu = manage (new Menu);
450 MenuList &layers_items = layers_menu->items();
451 layers_menu->set_name("ArdourContextMenu");
453 RadioMenuItem::Group layers_group;
455 /* Find out how many overlaid/stacked tracks we have in the selection */
459 TrackSelection const & s = _editor.get_selection().tracks;
460 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
461 StreamView* v = (*i)->view ();
466 switch (v->layer_display ()) {
477 /* We're not connecting to signal_toggled() here; in the case where these two items are
478 set to be in the `inconsistent' state, it seems that one or other will end up active
479 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
480 select the active one, no toggled signal is emitted so nothing happens.
483 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
484 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
485 i->set_active (overlaid != 0 && stacked == 0);
486 i->set_inconsistent (overlaid != 0 && stacked != 0);
487 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
489 layers_items.push_back (
490 RadioMenuElem (layers_group, _("Stacked"),
491 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
494 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
495 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
496 i->set_active (overlaid == 0 && stacked != 0);
497 i->set_inconsistent (overlaid != 0 && stacked != 0);
499 items.push_back (MenuElem (_("Layers"), *layers_menu));
501 if (!Profile->get_sae()) {
503 Menu* alignment_menu = manage (new Menu);
504 MenuList& alignment_items = alignment_menu->items();
505 alignment_menu->set_name ("ArdourContextMenu");
507 RadioMenuItem::Group align_group;
509 /* Same verbose hacks as for the layering options above */
515 boost::shared_ptr<Track> first_track;
517 TrackSelection const & s = _editor.get_selection().tracks;
518 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
519 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
520 if (!r || !r->is_track ()) {
525 first_track = r->track();
528 switch (r->track()->alignment_choice()) {
532 switch (r->track()->alignment_style()) {
533 case ExistingMaterial:
541 case UseExistingMaterial:
557 inconsistent = false;
566 if (!inconsistent && first_track) {
568 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
569 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
570 i->set_active (automatic != 0 && existing == 0 && capture == 0);
571 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
573 switch (first_track->alignment_choice()) {
575 switch (first_track->alignment_style()) {
576 case ExistingMaterial:
577 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
580 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
588 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
589 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
590 i->set_active (existing != 0 && capture == 0 && automatic == 0);
591 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing == 0 && capture != 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
598 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
604 Menu* mode_menu = manage (new Menu);
605 MenuList& mode_items = mode_menu->items ();
606 mode_menu->set_name ("ArdourContextMenu");
608 RadioMenuItem::Group mode_group;
614 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
615 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
616 if (!r || !r->is_track ()) {
620 switch (r->track()->mode()) {
633 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
636 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
637 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
639 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
640 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
642 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
643 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
645 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
646 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
648 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
649 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
651 items.push_back (MenuElem (_("Mode"), *mode_menu));
654 color_mode_menu = build_color_mode_menu();
655 if (color_mode_menu) {
656 items.push_back (MenuElem (_("Color Mode"), *color_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 CheckMenuItem* i = dynamic_cast<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));
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 SimpleRect (*canvas_display ());
796 timestretch_rect->property_x1() = 0.0;
797 timestretch_rect->property_y1() = 0.0;
798 timestretch_rect->property_x2() = 0.0;
799 timestretch_rect->property_y2() = 0.0;
800 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
801 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
804 timestretch_rect->show ();
805 timestretch_rect->raise_to_top ();
807 double const x1 = start / _editor.get_current_zoom();
808 double const x2 = (end - 1) / _editor.get_current_zoom();
810 timestretch_rect->property_x1() = x1;
811 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
812 timestretch_rect->property_x2() = x2;
813 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
817 RouteTimeAxisView::hide_timestretch ()
819 TimeAxisView::hide_timestretch ();
821 if (timestretch_rect) {
822 timestretch_rect->hide ();
827 RouteTimeAxisView::show_selection (TimeSelection& ts)
831 /* ignore it if our edit group is not active or if the selection was started
832 in some other track or route group (remember that route_group() == 0 means
833 that the track is not in an route group).
836 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
837 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
843 TimeAxisView::show_selection (ts);
847 RouteTimeAxisView::set_height (uint32_t h)
850 bool height_changed = (height == 0) || (h != height);
851 gm.get_level_meter().setup_meters (gmlen);
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_unit ()
920 set_samples_per_unit (_editor.get_current_zoom());
924 RouteTimeAxisView::horizontal_position_changed ()
927 _view->horizontal_position_changed ();
932 RouteTimeAxisView::set_samples_per_unit (double spu)
937 speed = track()->speed();
941 _view->set_samples_per_unit (spu * speed);
944 TimeAxisView::set_samples_per_unit (spu * speed);
948 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
950 if (!mitem->get_active()) {
951 /* this is one of the two calls made when these radio menu items change status. this one
952 is for the item that became inactive, and we want to ignore it.
957 if (apply_to_selection) {
958 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
961 track()->set_align_choice (choice);
967 RouteTimeAxisView::rename_current_playlist ()
969 ArdourPrompter prompter (true);
972 boost::shared_ptr<Track> tr = track();
973 if (!tr || tr->destructive()) {
977 boost::shared_ptr<Playlist> pl = tr->playlist();
982 prompter.set_title (_("Rename Playlist"));
983 prompter.set_prompt (_("New name for playlist:"));
984 prompter.set_initial_text (pl->name());
985 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
986 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
988 switch (prompter.run ()) {
989 case Gtk::RESPONSE_ACCEPT:
990 prompter.get_result (name);
1002 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1004 std::string ret (basename);
1006 std::string const group_string = "." + route_group()->name() + ".";
1008 // iterate through all playlists
1010 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1011 std::string tmp = (*i)->name();
1013 std::string::size_type idx = tmp.find(group_string);
1014 // find those which belong to this group
1015 if (idx != string::npos) {
1016 tmp = tmp.substr(idx + group_string.length());
1018 // and find the largest current number
1019 int x = atoi(tmp.c_str());
1020 if (x > maxnumber) {
1029 snprintf (buf, sizeof(buf), "%d", maxnumber);
1031 ret = this->name() + "." + route_group()->name () + "." + buf;
1037 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1041 boost::shared_ptr<Track> tr = track ();
1042 if (!tr || tr->destructive()) {
1046 boost::shared_ptr<const Playlist> pl = tr->playlist();
1053 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1054 name = resolve_new_group_playlist_name(name, playlists_before_op);
1057 while (_session->playlists->by_name(name)) {
1058 name = Playlist::bump_name (name, *_session);
1061 // TODO: The prompter "new" button should be de-activated if the user
1062 // specifies a playlist name which already exists in the session.
1066 ArdourPrompter prompter (true);
1068 prompter.set_title (_("New Copy Playlist"));
1069 prompter.set_prompt (_("Name for new playlist:"));
1070 prompter.set_initial_text (name);
1071 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1072 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1073 prompter.show_all ();
1075 switch (prompter.run ()) {
1076 case Gtk::RESPONSE_ACCEPT:
1077 prompter.get_result (name);
1085 if (name.length()) {
1086 tr->use_copy_playlist ();
1087 tr->playlist()->set_name (name);
1092 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1096 boost::shared_ptr<Track> tr = track ();
1097 if (!tr || tr->destructive()) {
1101 boost::shared_ptr<const Playlist> pl = tr->playlist();
1108 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1109 name = resolve_new_group_playlist_name(name,playlists_before_op);
1112 while (_session->playlists->by_name(name)) {
1113 name = Playlist::bump_name (name, *_session);
1119 ArdourPrompter prompter (true);
1121 prompter.set_title (_("New Playlist"));
1122 prompter.set_prompt (_("Name for new playlist:"));
1123 prompter.set_initial_text (name);
1124 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1125 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1127 switch (prompter.run ()) {
1128 case Gtk::RESPONSE_ACCEPT:
1129 prompter.get_result (name);
1137 if (name.length()) {
1138 tr->use_new_playlist ();
1139 tr->playlist()->set_name (name);
1144 RouteTimeAxisView::clear_playlist ()
1146 boost::shared_ptr<Track> tr = track ();
1147 if (!tr || tr->destructive()) {
1151 boost::shared_ptr<Playlist> pl = tr->playlist();
1156 _editor.clear_playlist (pl);
1160 RouteTimeAxisView::speed_changed ()
1162 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1166 RouteTimeAxisView::update_diskstream_display ()
1176 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1178 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1180 /* special case: select/deselect all tracks */
1181 if (_editor.get_selection().selected (this)) {
1182 _editor.get_selection().clear_tracks ();
1184 _editor.select_all_tracks ();
1190 switch (ArdourKeyboard::selection_type (ev->state)) {
1191 case Selection::Toggle:
1192 _editor.get_selection().toggle (this);
1195 case Selection::Set:
1196 _editor.get_selection().set (this);
1199 case Selection::Extend:
1200 _editor.extend_selection_to_track (*this);
1203 case Selection::Add:
1204 _editor.get_selection().add (this);
1210 RouteTimeAxisView::set_selected_points (PointSelection& points)
1212 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1213 (*i)->set_selected_points (points);
1218 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1221 _view->set_selected_regionviews (regions);
1225 /** Add the selectable things that we have to a list.
1226 * @param results List to add things to.
1229 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1234 speed = track()->speed();
1237 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1238 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1240 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1241 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1244 /* pick up visible automation tracks */
1246 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1247 if (!(*i)->hidden()) {
1248 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1254 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1257 _view->get_inverted_selectables (sel, results);
1260 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1261 if (!(*i)->hidden()) {
1262 (*i)->get_inverted_selectables (sel, results);
1270 RouteTimeAxisView::route_group () const
1272 return _route->route_group();
1276 RouteTimeAxisView::name() const
1278 return _route->name();
1281 boost::shared_ptr<Playlist>
1282 RouteTimeAxisView::playlist () const
1284 boost::shared_ptr<Track> tr;
1286 if ((tr = track()) != 0) {
1287 return tr->playlist();
1289 return boost::shared_ptr<Playlist> ();
1294 RouteTimeAxisView::name_entry_changed ()
1296 string x = name_entry.get_text ();
1298 if (x == _route->name()) {
1302 strip_whitespace_edges (x);
1304 if (x.length() == 0) {
1305 name_entry.set_text (_route->name());
1309 if (_session->route_name_internal (x)) {
1310 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1312 name_entry.grab_focus ();
1313 } else if (RouteUI::verify_new_route_name (x)) {
1314 _route->set_name (x);
1316 name_entry.grab_focus ();
1320 boost::shared_ptr<Region>
1321 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1323 boost::shared_ptr<Playlist> pl = playlist ();
1326 return pl->find_next_region (pos, point, dir);
1329 return boost::shared_ptr<Region> ();
1333 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1335 boost::shared_ptr<Playlist> pl = playlist ();
1338 return pl->find_next_region_boundary (pos, dir);
1345 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1347 boost::shared_ptr<Playlist> what_we_got;
1348 boost::shared_ptr<Track> tr = track ();
1349 boost::shared_ptr<Playlist> playlist;
1352 /* route is a bus, not a track */
1356 playlist = tr->playlist();
1358 TimeSelection time (selection.time);
1359 float const speed = tr->speed();
1360 if (speed != 1.0f) {
1361 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1362 (*i).start = session_frame_to_track_frame((*i).start, speed);
1363 (*i).end = session_frame_to_track_frame((*i).end, speed);
1367 playlist->clear_changes ();
1368 playlist->clear_owned_changes ();
1372 if (playlist->cut (time) != 0) {
1373 vector<Command*> cmds;
1374 playlist->rdiff (cmds);
1375 _session->add_commands (cmds);
1377 _session->add_command (new StatefulDiffCommand (playlist));
1382 if ((what_we_got = playlist->cut (time)) != 0) {
1383 _editor.get_cut_buffer().add (what_we_got);
1384 vector<Command*> cmds;
1385 playlist->rdiff (cmds);
1386 _session->add_commands (cmds);
1388 _session->add_command (new StatefulDiffCommand (playlist));
1392 if ((what_we_got = playlist->copy (time)) != 0) {
1393 _editor.get_cut_buffer().add (what_we_got);
1398 if ((what_we_got = playlist->cut (time)) != 0) {
1400 vector<Command*> cmds;
1401 playlist->rdiff (cmds);
1402 _session->add_commands (cmds);
1403 _session->add_command (new StatefulDiffCommand (playlist));
1404 what_we_got->release ();
1411 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1417 boost::shared_ptr<Playlist> pl = playlist ();
1418 PlaylistSelection::iterator p;
1420 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1422 if (p == selection.playlists.end()) {
1426 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1428 if (track()->speed() != 1.0f) {
1429 pos = session_frame_to_track_frame (pos, track()->speed());
1430 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1433 pl->clear_changes ();
1434 pl->paste (*p, pos, times);
1435 _session->add_command (new StatefulDiffCommand (pl));
1441 struct PlaylistSorter {
1442 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1443 return a->sort_id() < b->sort_id();
1448 RouteTimeAxisView::build_playlist_menu ()
1450 using namespace Menu_Helpers;
1456 delete playlist_action_menu;
1457 playlist_action_menu = new Menu;
1458 playlist_action_menu->set_name ("ArdourContextMenu");
1460 MenuList& playlist_items = playlist_action_menu->items();
1461 playlist_action_menu->set_name ("ArdourContextMenu");
1462 playlist_items.clear();
1464 RadioMenuItem::Group playlist_group;
1465 boost::shared_ptr<Track> tr = track ();
1467 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1469 /* sort the playlists */
1471 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1473 /* add the playlists to the menu */
1474 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1475 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1476 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1477 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1479 if (tr->playlist()->id() == (*i)->id()) {
1485 playlist_items.push_back (SeparatorElem());
1486 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1487 playlist_items.push_back (SeparatorElem());
1489 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1490 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1491 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1494 // Use a label which tells the user what is happening
1495 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1496 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1500 playlist_items.push_back (SeparatorElem());
1501 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1502 playlist_items.push_back (SeparatorElem());
1504 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1508 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1510 assert (is_track());
1512 // exit if we were triggered by deactivating the old playlist
1513 if (!item->get_active()) {
1517 boost::shared_ptr<Playlist> pl (wpl.lock());
1523 if (track()->playlist() == pl) {
1524 // exit when use_playlist is called by the creation of the playlist menu
1525 // or the playlist choice is unchanged
1529 track()->use_playlist (pl);
1531 RouteGroup* rg = route_group();
1533 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1534 std::string group_string = "." + rg->name() + ".";
1536 std::string take_name = pl->name();
1537 std::string::size_type idx = take_name.find(group_string);
1539 if (idx == std::string::npos)
1542 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1544 boost::shared_ptr<RouteList> rl (rg->route_list());
1546 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1547 if ( (*i) == this->route()) {
1551 std::string playlist_name = (*i)->name()+group_string+take_name;
1553 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1558 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1560 // No playlist for this track for this take yet, make it
1561 track->use_new_playlist();
1562 track->playlist()->set_name(playlist_name);
1564 track->use_playlist(ipl);
1571 RouteTimeAxisView::show_playlist_selector ()
1573 _editor.playlist_selector().show_for (this);
1577 RouteTimeAxisView::map_frozen ()
1583 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1585 switch (track()->freeze_state()) {
1587 playlist_button.set_sensitive (false);
1588 rec_enable_button->set_sensitive (false);
1591 playlist_button.set_sensitive (true);
1592 rec_enable_button->set_sensitive (true);
1598 RouteTimeAxisView::color_handler ()
1600 //case cTimeStretchOutline:
1601 if (timestretch_rect) {
1602 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1604 //case cTimeStretchFill:
1605 if (timestretch_rect) {
1606 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1612 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1613 * Will add track if necessary.
1616 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1618 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1619 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1622 /* it doesn't exist yet, so we don't care about the button state: just add it */
1623 create_automation_child (param, true);
1626 bool yn = menu->get_active();
1627 bool changed = false;
1629 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1631 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1632 will have done that for us.
1635 if (changed && !no_redraw) {
1643 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1645 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1651 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1653 if (menu && !_hidden) {
1654 ignore_toggle = true;
1655 menu->set_active (false);
1656 ignore_toggle = false;
1659 if (_route && !no_redraw) {
1666 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1668 if (apply_to_selection) {
1669 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1673 /* Show our automation */
1675 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1676 i->second->set_marked_for_display (true);
1678 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1681 menu->set_active(true);
1686 /* Show processor automation */
1688 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1689 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1690 if ((*ii)->view == 0) {
1691 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1694 (*ii)->menu_item->set_active (true);
1707 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1709 if (apply_to_selection) {
1710 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1714 /* Show our automation */
1716 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1717 if (i->second->has_automation()) {
1718 i->second->set_marked_for_display (true);
1720 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1722 menu->set_active(true);
1727 /* Show processor automation */
1729 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1730 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1731 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1732 (*ii)->menu_item->set_active (true);
1744 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1746 if (apply_to_selection) {
1747 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1751 /* Hide our automation */
1753 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1754 i->second->set_marked_for_display (false);
1756 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1759 menu->set_active (false);
1763 /* Hide processor automation */
1765 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1766 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1767 (*ii)->menu_item->set_active (false);
1778 RouteTimeAxisView::region_view_added (RegionView* rv)
1780 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1781 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1782 boost::shared_ptr<AutomationTimeAxisView> atv;
1784 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1789 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1790 (*i)->add_ghost(rv);
1794 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1796 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1802 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1804 parent.remove_processor_automation_node (this);
1808 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1811 remove_child (pan->view);
1815 RouteTimeAxisView::ProcessorAutomationNode*
1816 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1818 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1820 if ((*i)->processor == processor) {
1822 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1823 if ((*ii)->what == what) {
1833 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1835 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1838 ProcessorAutomationNode* pan;
1840 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1841 /* session state may never have been saved with new plugin */
1842 error << _("programming error: ")
1843 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1844 processor->name(), what.type(), (int) what.channel(), what.id() )
1854 boost::shared_ptr<AutomationControl> control
1855 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1857 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1858 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1859 _editor, *this, false, parent_canvas,
1860 processor->describe_parameter (what), processor->name()));
1862 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1864 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1867 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1872 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1875 pan->menu_item->set_active (false);
1884 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1886 boost::shared_ptr<Processor> processor (p.lock ());
1888 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1889 /* The Amp processor is a special case and is dealt with separately */
1893 set<Evoral::Parameter> existing;
1895 processor->what_has_data (existing);
1897 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1899 Evoral::Parameter param (*i);
1900 boost::shared_ptr<AutomationLine> al;
1902 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1905 add_processor_automation_curve (processor, param);
1911 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1913 using namespace Menu_Helpers;
1917 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1919 _automation_tracks[param] = track;
1921 /* existing state overrides "show" argument */
1922 string s = track->gui_property ("visible");
1924 show = string_is_affirmative (s);
1927 /* this might or might not change the visibility status, so don't rely on it */
1928 track->set_marked_for_display (show);
1930 if (show && !no_redraw) {
1934 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1935 /* MIDI-related parameters are always in the menu, there's no
1936 reason to rebuild the menu just because we added a automation
1937 lane for one of them. But if we add a non-MIDI automation
1938 lane, then we need to invalidate the display menu.
1940 delete display_menu;
1946 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1948 boost::shared_ptr<Processor> processor (p.lock ());
1950 if (!processor || !processor->display_to_user ()) {
1954 /* we use this override to veto the Amp processor from the plugin menu,
1955 as its automation lane can be accessed using the special "Fader" menu
1959 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1963 using namespace Menu_Helpers;
1964 ProcessorAutomationInfo *rai;
1965 list<ProcessorAutomationInfo*>::iterator x;
1967 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1969 if (automatable.empty()) {
1973 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1974 if ((*x)->processor == processor) {
1979 if (x == processor_automation.end()) {
1981 rai = new ProcessorAutomationInfo (processor);
1982 processor_automation.push_back (rai);
1990 /* any older menu was deleted at the top of processors_changed()
1991 when we cleared the subplugin menu.
1994 rai->menu = manage (new Menu);
1995 MenuList& items = rai->menu->items();
1996 rai->menu->set_name ("ArdourContextMenu");
2000 std::set<Evoral::Parameter> has_visible_automation;
2001 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2003 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2005 ProcessorAutomationNode* pan;
2006 CheckMenuItem* mitem;
2008 string name = processor->describe_parameter (*i);
2010 items.push_back (CheckMenuElem (name));
2011 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2013 _subplugin_menu_map[*i] = mitem;
2015 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2016 mitem->set_active(true);
2019 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2023 pan = new ProcessorAutomationNode (*i, mitem, *this);
2025 rai->lines.push_back (pan);
2029 pan->menu_item = mitem;
2033 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2036 /* add the menu for this processor, because the subplugin
2037 menu is always cleared at the top of processors_changed().
2038 this is the result of some poor design in gtkmm and/or
2042 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2047 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2048 RouteTimeAxisView::ProcessorAutomationNode* pan)
2050 bool showit = pan->menu_item->get_active();
2051 bool redraw = false;
2053 if (pan->view == 0 && showit) {
2054 add_processor_automation_curve (rai->processor, pan->what);
2058 if (pan->view && pan->view->set_marked_for_display (showit)) {
2062 if (redraw && !no_redraw) {
2068 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2070 if (c.type == RouteProcessorChange::MeterPointChange) {
2071 /* nothing to do if only the meter point has changed */
2075 using namespace Menu_Helpers;
2077 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2078 (*i)->valid = false;
2081 setup_processor_menu_and_curves ();
2083 bool deleted_processor_automation = false;
2085 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2087 list<ProcessorAutomationInfo*>::iterator tmp;
2095 processor_automation.erase (i);
2096 deleted_processor_automation = true;
2103 if (deleted_processor_automation && !no_redraw) {
2108 boost::shared_ptr<AutomationLine>
2109 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2111 ProcessorAutomationNode* pan;
2113 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2119 return boost::shared_ptr<AutomationLine>();
2123 RouteTimeAxisView::reset_processor_automation_curves ()
2125 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2131 RouteTimeAxisView::update_rec_display ()
2133 RouteUI::update_rec_display ();
2134 name_entry.set_sensitive (!_route->record_enabled());
2138 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2140 if (apply_to_selection) {
2141 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2145 _view->set_layer_display (d);
2148 set_gui_property (X_("layer-display"), enum_2_string (d));
2153 RouteTimeAxisView::layer_display () const
2156 return _view->layer_display ();
2159 /* we don't know, since we don't have a _view, so just return something */
2165 boost::shared_ptr<AutomationTimeAxisView>
2166 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2168 AutomationTracks::iterator i = _automation_tracks.find(param);
2169 if (i != _automation_tracks.end()) {
2172 return boost::shared_ptr<AutomationTimeAxisView>();
2177 RouteTimeAxisView::fast_update ()
2179 gm.get_level_meter().update_meters ();
2183 RouteTimeAxisView::hide_meter ()
2186 gm.get_level_meter().hide_meters ();
2190 RouteTimeAxisView::show_meter ()
2196 RouteTimeAxisView::reset_meter ()
2198 if (Config->get_show_track_meters()) {
2199 gm.get_level_meter().setup_meters (height-5);
2206 RouteTimeAxisView::clear_meter ()
2208 gm.get_level_meter().clear_meters ();
2212 RouteTimeAxisView::meter_changed ()
2214 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2219 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2225 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2227 using namespace Menu_Helpers;
2229 if (!_underlay_streams.empty()) {
2230 MenuList& parent_items = parent_menu->items();
2231 Menu* gs_menu = manage (new Menu);
2232 gs_menu->set_name ("ArdourContextMenu");
2233 MenuList& gs_items = gs_menu->items();
2235 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2237 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2238 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2239 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2245 RouteTimeAxisView::set_underlay_state()
2247 if (!underlay_xml_node) {
2251 XMLNodeList nlist = underlay_xml_node->children();
2252 XMLNodeConstIterator niter;
2253 XMLNode *child_node;
2255 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2256 child_node = *niter;
2258 if (child_node->name() != "Underlay") {
2262 XMLProperty* prop = child_node->property ("id");
2264 PBD::ID id (prop->value());
2266 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2269 add_underlay(v->view(), false);
2278 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2284 RouteTimeAxisView& other = v->trackview();
2286 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2287 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2288 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2292 _underlay_streams.push_back(v);
2293 other._underlay_mirrors.push_back(this);
2295 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2297 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2299 if (!underlay_xml_node) {
2300 underlay_xml_node = xml_node->add_child("Underlays");
2303 XMLNode* node = underlay_xml_node->add_child("Underlay");
2304 XMLProperty* prop = node->add_property("id");
2305 prop->set_value(v->trackview().route()->id().to_s());
2312 RouteTimeAxisView::remove_underlay (StreamView* v)
2318 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2319 RouteTimeAxisView& other = v->trackview();
2321 if (it != _underlay_streams.end()) {
2322 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2324 if (gm == other._underlay_mirrors.end()) {
2325 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2329 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2331 _underlay_streams.erase(it);
2332 other._underlay_mirrors.erase(gm);
2334 if (underlay_xml_node) {
2335 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2341 RouteTimeAxisView::set_button_names ()
2343 if (_route && _route->solo_safe()) {
2344 solo_button->remove ();
2345 if (solo_safe_pixbuf == 0) {
2346 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2348 solo_button->set_image (solo_safe_pixbuf);
2349 solo_button->set_text (string());
2351 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2352 if (Config->get_solo_control_is_listen_control()) {
2353 switch (Config->get_listen_position()) {
2354 case AfterFaderListen:
2355 solo_button->set_text (_("A"));
2356 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2358 case PreFaderListen:
2359 solo_button->set_text (_("P"));
2360 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2364 solo_button->set_text (_("s"));
2365 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2368 mute_button->set_text (_("m"));
2372 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2374 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2375 if (i != _main_automation_menu_map.end()) {
2379 i = _subplugin_menu_map.find (param);
2380 if (i != _subplugin_menu_map.end()) {
2388 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2390 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2392 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2396 gain_track.reset (new AutomationTimeAxisView (_session,
2397 _route, _route->amp(), c, param,
2402 _route->amp()->describe_parameter(param)));
2405 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2408 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2412 void add_region_to_list (RegionView* rv, RegionList* l)
2414 l->push_back (rv->region());
2418 RouteTimeAxisView::combine_regions ()
2420 /* as of may 2011, we do not offer uncombine for MIDI tracks
2423 if (!is_audio_track()) {
2431 RegionList selected_regions;
2432 boost::shared_ptr<Playlist> playlist = track()->playlist();
2434 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2436 if (selected_regions.size() < 2) {
2440 playlist->clear_changes ();
2441 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2443 _session->add_command (new StatefulDiffCommand (playlist));
2444 /* make the new region be selected */
2446 return _view->find_view (compound_region);
2450 RouteTimeAxisView::uncombine_regions ()
2452 /* as of may 2011, we do not offer uncombine for MIDI tracks
2454 if (!is_audio_track()) {
2462 RegionList selected_regions;
2463 boost::shared_ptr<Playlist> playlist = track()->playlist();
2465 /* have to grab selected regions first because the uncombine is going
2466 * to change that in the middle of the list traverse
2469 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2471 playlist->clear_changes ();
2473 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2474 playlist->uncombine (*i);
2477 _session->add_command (new StatefulDiffCommand (playlist));
2481 RouteTimeAxisView::state_id() const
2483 return string_compose ("rtav %1", _route->id().to_s());
2488 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2490 TimeAxisView::remove_child (c);
2492 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2494 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2495 if (i->second == a) {
2496 _automation_tracks.erase (i);