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"
61 #include "gui_thread.h"
63 #include "playlist_selector.h"
64 #include "point_selection.h"
66 #include "public_editor.h"
67 #include "region_view.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "streamview.h"
72 #include "route_group_menu.h"
74 #include "ardour/track.h"
78 using namespace ARDOUR;
80 using namespace Gtkmm2ext;
82 using namespace Editing;
86 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
89 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
91 , parent_canvas (canvas)
94 , route_group_button (_("g"))
95 , playlist_button (_("p"))
96 , automation_button (_("a"))
97 , automation_action_menu (0)
98 , plugins_submenu_item (0)
99 , route_group_menu (0)
100 , playlist_action_menu (0)
102 , color_mode_menu (0)
103 , gm (sess, true, 125, 18)
104 , _ignore_set_layer_display (false)
109 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
111 RouteUI::set_route (rt);
113 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
114 gm.get_level_meter().set_no_show_all();
115 gm.get_level_meter().setup_meters(50);
116 gm.update_gain_sensitive ();
118 string str = gui_property ("height");
120 set_height (atoi (str));
122 set_height (preset_height (HeightNormal));
125 if (!_route->is_auditioner()) {
126 if (gui_property ("visible").empty()) {
127 set_gui_property ("visible", true);
130 set_gui_property ("visible", false);
134 update_solo_display ();
136 timestretch_rect = 0;
139 ignore_toggle = false;
141 route_group_button.set_name ("route button");
142 playlist_button.set_name ("route button");
143 automation_button.set_name ("route button");
145 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
146 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
147 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
153 switch (track()->mode()) {
155 case ARDOUR::NonLayered:
156 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
158 case ARDOUR::Destructive:
159 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
163 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
165 if (is_midi_track()) {
166 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
167 gm.set_fader_name ("MidiTrackFader");
169 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
170 gm.set_fader_name ("AudioTrackFader");
173 rec_enable_button->set_sensitive (_session->writable());
175 /* set playlist button tip to the current playlist, and make it update when it changes */
176 update_playlist_tip ();
177 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
180 gm.set_fader_name ("AudioBusFader");
183 controls_hbox.pack_start(gm.get_level_meter(), false, false);
184 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
185 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
186 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
188 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
190 if (!_route->is_master()) {
191 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
194 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
195 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
197 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
198 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
199 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
201 if (is_midi_track()) {
202 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
204 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
209 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
211 if (is_track() && track()->mode() == ARDOUR::Normal) {
212 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
217 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
218 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
222 str = gui_property ("layer-display");
224 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
227 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
228 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
230 /* pick up the correct freeze state */
235 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_frames_per_pixel));
236 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
237 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
239 PropertyList* plist = new PropertyList();
241 plist->add (ARDOUR::Properties::mute, true);
242 plist->add (ARDOUR::Properties::solo, true);
244 route_group_menu = new RouteGroupMenu (_session, plist);
246 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
248 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
251 RouteTimeAxisView::~RouteTimeAxisView ()
253 CatchDeletion (this);
255 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
259 delete playlist_action_menu;
260 playlist_action_menu = 0;
265 _automation_tracks.clear ();
267 delete route_group_menu;
271 RouteTimeAxisView::post_construct ()
273 /* map current state of the route */
275 update_diskstream_display ();
276 setup_processor_menu_and_curves ();
277 reset_processor_automation_curves ();
280 /** Set up the processor menu for the current set of processors, and
281 * display automation curves for any parameters which have data.
284 RouteTimeAxisView::setup_processor_menu_and_curves ()
286 _subplugin_menu_map.clear ();
287 subplugin_menu.items().clear ();
288 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
289 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
293 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
295 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
296 if (_route->route_group()) {
297 _route->route_group()->remove (_route);
303 r.push_back (route ());
305 route_group_menu->build (r);
306 route_group_menu->menu()->popup (ev->button, ev->time);
312 RouteTimeAxisView::playlist_changed ()
318 RouteTimeAxisView::label_view ()
320 string x = _route->name();
322 if (x != name_label.get_text()) {
323 name_label.set_text (x);
329 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
331 if (what_changed.contains (ARDOUR::Properties::name)) {
337 RouteTimeAxisView::take_name_changed (void *src)
345 RouteTimeAxisView::playlist_click ()
347 build_playlist_menu ();
348 conditionally_add_to_selection ();
349 playlist_action_menu->popup (1, gtk_get_current_event_time());
353 RouteTimeAxisView::automation_click ()
355 conditionally_add_to_selection ();
356 build_automation_action_menu (false);
357 automation_action_menu->popup (1, gtk_get_current_event_time());
361 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
363 using namespace Menu_Helpers;
365 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
366 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
369 detach_menu (subplugin_menu);
371 _main_automation_menu_map.clear ();
372 delete automation_action_menu;
373 automation_action_menu = new Menu;
375 MenuList& items = automation_action_menu->items();
377 automation_action_menu->set_name ("ArdourContextMenu");
379 items.push_back (MenuElem (_("Show All Automation"),
380 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
382 items.push_back (MenuElem (_("Show Existing Automation"),
383 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
385 items.push_back (MenuElem (_("Hide All Automation"),
386 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
388 /* Attach the plugin submenu. It may have previously been used elsewhere,
389 so it was detached above
392 if (!subplugin_menu.items().empty()) {
393 items.push_back (SeparatorElem ());
394 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
395 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
400 RouteTimeAxisView::build_display_menu ()
402 using namespace Menu_Helpers;
406 TimeAxisView::build_display_menu ();
408 /* now fill it with our stuff */
410 MenuList& items = display_menu->items();
411 display_menu->set_name ("ArdourContextMenu");
413 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
416 detach_menu (*_size_menu);
419 items.push_back (MenuElem (_("Height"), *_size_menu));
421 items.push_back (SeparatorElem());
423 if (!Profile->get_sae()) {
424 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
425 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
426 items.push_back (SeparatorElem());
429 // Hook for derived classes to add type specific stuff
430 append_extra_display_menu_items ();
434 Menu* layers_menu = manage (new Menu);
435 MenuList &layers_items = layers_menu->items();
436 layers_menu->set_name("ArdourContextMenu");
438 RadioMenuItem::Group layers_group;
440 /* Find out how many overlaid/stacked tracks we have in the selection */
444 TrackSelection const & s = _editor.get_selection().tracks;
445 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
446 StreamView* v = (*i)->view ();
451 switch (v->layer_display ()) {
462 /* We're not connecting to signal_toggled() here; in the case where these two items are
463 set to be in the `inconsistent' state, it seems that one or other will end up active
464 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
465 select the active one, no toggled signal is emitted so nothing happens.
468 _ignore_set_layer_display = true;
470 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
471 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
472 i->set_active (overlaid != 0 && stacked == 0);
473 i->set_inconsistent (overlaid != 0 && stacked != 0);
474 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
476 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
477 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
478 i->set_active (overlaid == 0 && stacked != 0);
479 i->set_inconsistent (overlaid != 0 && stacked != 0);
480 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
482 _ignore_set_layer_display = false;
484 items.push_back (MenuElem (_("Layers"), *layers_menu));
486 if (!Profile->get_sae()) {
488 Menu* alignment_menu = manage (new Menu);
489 MenuList& alignment_items = alignment_menu->items();
490 alignment_menu->set_name ("ArdourContextMenu");
492 RadioMenuItem::Group align_group;
494 /* Same verbose hacks as for the layering options above */
500 boost::shared_ptr<Track> first_track;
502 TrackSelection const & s = _editor.get_selection().tracks;
503 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
504 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
505 if (!r || !r->is_track ()) {
510 first_track = r->track();
513 switch (r->track()->alignment_choice()) {
517 switch (r->track()->alignment_style()) {
518 case ExistingMaterial:
526 case UseExistingMaterial:
542 inconsistent = false;
551 if (!inconsistent && first_track) {
553 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
554 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
555 i->set_active (automatic != 0 && existing == 0 && capture == 0);
556 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
558 switch (first_track->alignment_choice()) {
560 switch (first_track->alignment_style()) {
561 case ExistingMaterial:
562 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
565 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
573 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
574 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
575 i->set_active (existing != 0 && capture == 0 && automatic == 0);
576 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
578 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
579 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
580 i->set_active (existing == 0 && capture != 0 && automatic == 0);
581 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
583 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
589 Menu* mode_menu = manage (new Menu);
590 MenuList& mode_items = mode_menu->items ();
591 mode_menu->set_name ("ArdourContextMenu");
593 RadioMenuItem::Group mode_group;
599 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
600 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
601 if (!r || !r->is_track ()) {
605 switch (r->track()->mode()) {
618 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
619 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
620 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
621 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
622 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
624 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
625 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
626 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
627 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
628 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
630 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
631 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
632 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
633 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
634 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
636 items.push_back (MenuElem (_("Mode"), *mode_menu));
640 items.push_back (SeparatorElem());
642 build_playlist_menu ();
643 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
644 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
647 route_group_menu->detach ();
650 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
651 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
653 r.push_back (rtv->route ());
658 r.push_back (route ());
661 route_group_menu->build (r);
662 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
664 build_automation_action_menu (true);
665 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
667 items.push_back (SeparatorElem());
671 TrackSelection const & s = _editor.get_selection().tracks;
672 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
673 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
678 if (r->route()->active()) {
685 items.push_back (CheckMenuElem (_("Active")));
686 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
687 bool click_sets_active = true;
688 if (active > 0 && inactive == 0) {
689 i->set_active (true);
690 click_sets_active = false;
691 } else if (active > 0 && inactive > 0) {
692 i->set_inconsistent (true);
694 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
696 items.push_back (SeparatorElem());
697 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
698 if (!Profile->get_sae()) {
699 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
701 items.push_front (SeparatorElem());
702 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
707 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
709 if (apply_to_selection) {
710 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
715 if (!track()->can_use_mode (mode, needs_bounce)) {
721 cerr << "would bounce this one\n";
726 track()->set_mode (mode);
728 rec_enable_button->remove ();
731 case ARDOUR::NonLayered:
733 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
734 rec_enable_button->set_text (string());
736 case ARDOUR::Destructive:
737 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
738 rec_enable_button->set_text (string());
742 rec_enable_button->show_all ();
747 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
749 TimeAxisView::show_timestretch (start, end, layers, layer);
759 /* check that the time selection was made in our route, or our route group.
760 remember that route_group() == 0 implies the route is *not* in a edit group.
763 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
764 /* this doesn't apply to us */
768 /* ignore it if our edit group is not active */
770 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
775 if (timestretch_rect == 0) {
776 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
777 timestretch_rect->set_fill_color (ARDOUR_UI::config()->canvasvar_TimeStretchFill.get());
778 timestretch_rect->set_outline_color (ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get());
781 timestretch_rect->show ();
782 timestretch_rect->raise_to_top ();
784 double const x1 = start / _editor.get_current_zoom();
785 double const x2 = (end - 1) / _editor.get_current_zoom();
787 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
788 x2, current_height() * (layers - layer) / layers));
792 RouteTimeAxisView::hide_timestretch ()
794 TimeAxisView::hide_timestretch ();
796 if (timestretch_rect) {
797 timestretch_rect->hide ();
802 RouteTimeAxisView::show_selection (TimeSelection& ts)
806 /* ignore it if our edit group is not active or if the selection was started
807 in some other track or route group (remember that route_group() == 0 means
808 that the track is not in an route group).
811 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
812 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
818 TimeAxisView::show_selection (ts);
822 RouteTimeAxisView::set_height (uint32_t h)
825 bool height_changed = (height == 0) || (h != height);
826 gm.get_level_meter().setup_meters (gmlen);
828 TimeAxisView::set_height (h);
831 _view->set_height ((double) current_height());
834 if (height >= preset_height (HeightNormal)) {
838 gm.get_gain_slider().show();
840 if (!_route || _route->is_monitor()) {
845 if (rec_enable_button)
846 rec_enable_button->show();
848 route_group_button.show();
849 automation_button.show();
851 if (is_track() && track()->mode() == ARDOUR::Normal) {
852 playlist_button.show();
859 gm.get_gain_slider().hide();
861 if (!_route || _route->is_monitor()) {
866 if (rec_enable_button)
867 rec_enable_button->show();
869 route_group_button.hide ();
870 automation_button.hide ();
872 if (is_track() && track()->mode() == ARDOUR::Normal) {
873 playlist_button.hide ();
878 if (height_changed && !no_redraw) {
879 /* only emit the signal if the height really changed */
885 RouteTimeAxisView::route_color_changed ()
888 _view->apply_color (color(), StreamView::RegionColor);
893 RouteTimeAxisView::reset_frames_per_pixel ()
895 set_frames_per_pixel (_editor.get_current_zoom());
899 RouteTimeAxisView::horizontal_position_changed ()
902 _view->horizontal_position_changed ();
907 RouteTimeAxisView::set_frames_per_pixel (double fpp)
912 speed = track()->speed();
916 _view->set_frames_per_pixel (fpp * speed);
919 TimeAxisView::set_frames_per_pixel (fpp * speed);
923 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
925 if (!mitem->get_active()) {
926 /* this is one of the two calls made when these radio menu items change status. this one
927 is for the item that became inactive, and we want to ignore it.
932 if (apply_to_selection) {
933 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
936 track()->set_align_choice (choice);
942 RouteTimeAxisView::rename_current_playlist ()
944 ArdourPrompter prompter (true);
947 boost::shared_ptr<Track> tr = track();
948 if (!tr || tr->destructive()) {
952 boost::shared_ptr<Playlist> pl = tr->playlist();
957 prompter.set_title (_("Rename Playlist"));
958 prompter.set_prompt (_("New name for playlist:"));
959 prompter.set_initial_text (pl->name());
960 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
961 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
963 switch (prompter.run ()) {
964 case Gtk::RESPONSE_ACCEPT:
965 prompter.get_result (name);
977 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
979 std::string ret (basename);
981 std::string const group_string = "." + route_group()->name() + ".";
983 // iterate through all playlists
985 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
986 std::string tmp = (*i)->name();
988 std::string::size_type idx = tmp.find(group_string);
989 // find those which belong to this group
990 if (idx != string::npos) {
991 tmp = tmp.substr(idx + group_string.length());
993 // and find the largest current number
994 int x = atoi(tmp.c_str());
1004 snprintf (buf, sizeof(buf), "%d", maxnumber);
1006 ret = this->name() + "." + route_group()->name () + "." + buf;
1012 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1016 boost::shared_ptr<Track> tr = track ();
1017 if (!tr || tr->destructive()) {
1021 boost::shared_ptr<const Playlist> pl = tr->playlist();
1028 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1029 name = resolve_new_group_playlist_name(name, playlists_before_op);
1032 while (_session->playlists->by_name(name)) {
1033 name = Playlist::bump_name (name, *_session);
1036 // TODO: The prompter "new" button should be de-activated if the user
1037 // specifies a playlist name which already exists in the session.
1041 ArdourPrompter prompter (true);
1043 prompter.set_title (_("New Copy Playlist"));
1044 prompter.set_prompt (_("Name for new playlist:"));
1045 prompter.set_initial_text (name);
1046 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1047 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1048 prompter.show_all ();
1050 switch (prompter.run ()) {
1051 case Gtk::RESPONSE_ACCEPT:
1052 prompter.get_result (name);
1060 if (name.length()) {
1061 tr->use_copy_playlist ();
1062 tr->playlist()->set_name (name);
1067 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1071 boost::shared_ptr<Track> tr = track ();
1072 if (!tr || tr->destructive()) {
1076 boost::shared_ptr<const Playlist> pl = tr->playlist();
1083 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1084 name = resolve_new_group_playlist_name(name,playlists_before_op);
1087 while (_session->playlists->by_name(name)) {
1088 name = Playlist::bump_name (name, *_session);
1094 ArdourPrompter prompter (true);
1096 prompter.set_title (_("New Playlist"));
1097 prompter.set_prompt (_("Name for new playlist:"));
1098 prompter.set_initial_text (name);
1099 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1100 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1102 switch (prompter.run ()) {
1103 case Gtk::RESPONSE_ACCEPT:
1104 prompter.get_result (name);
1112 if (name.length()) {
1113 tr->use_new_playlist ();
1114 tr->playlist()->set_name (name);
1119 RouteTimeAxisView::clear_playlist ()
1121 boost::shared_ptr<Track> tr = track ();
1122 if (!tr || tr->destructive()) {
1126 boost::shared_ptr<Playlist> pl = tr->playlist();
1131 _editor.clear_playlist (pl);
1135 RouteTimeAxisView::speed_changed ()
1137 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_frames_per_pixel, this));
1141 RouteTimeAxisView::update_diskstream_display ()
1151 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1153 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1155 /* special case: select/deselect all tracks */
1156 if (_editor.get_selection().selected (this)) {
1157 _editor.get_selection().clear_tracks ();
1159 _editor.select_all_tracks ();
1165 switch (ArdourKeyboard::selection_type (ev->state)) {
1166 case Selection::Toggle:
1167 _editor.get_selection().toggle (this);
1170 case Selection::Set:
1171 _editor.get_selection().set (this);
1174 case Selection::Extend:
1175 _editor.extend_selection_to_track (*this);
1178 case Selection::Add:
1179 _editor.get_selection().add (this);
1185 RouteTimeAxisView::set_selected_points (PointSelection& points)
1187 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1188 (*i)->set_selected_points (points);
1193 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1196 _view->set_selected_regionviews (regions);
1200 /** Add the selectable things that we have to a list.
1201 * @param results List to add things to.
1204 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1209 speed = track()->speed();
1212 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1213 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1215 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1216 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1219 /* pick up visible automation tracks */
1221 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1222 if (!(*i)->hidden()) {
1223 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1229 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1232 _view->get_inverted_selectables (sel, results);
1235 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1236 if (!(*i)->hidden()) {
1237 (*i)->get_inverted_selectables (sel, results);
1245 RouteTimeAxisView::route_group () const
1247 return _route->route_group();
1251 RouteTimeAxisView::name() const
1253 return _route->name();
1256 boost::shared_ptr<Playlist>
1257 RouteTimeAxisView::playlist () const
1259 boost::shared_ptr<Track> tr;
1261 if ((tr = track()) != 0) {
1262 return tr->playlist();
1264 return boost::shared_ptr<Playlist> ();
1269 RouteTimeAxisView::name_entry_changed ()
1271 TimeAxisView::name_entry_changed ();
1273 string x = name_entry->get_text ();
1275 if (x == _route->name()) {
1279 strip_whitespace_edges (x);
1281 if (x.length() == 0) {
1282 name_entry->set_text (_route->name());
1286 if (_session->route_name_internal (x)) {
1287 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1289 name_entry->grab_focus ();
1290 } else if (RouteUI::verify_new_route_name (x)) {
1291 _route->set_name (x);
1293 name_entry->grab_focus ();
1297 boost::shared_ptr<Region>
1298 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1300 boost::shared_ptr<Playlist> pl = playlist ();
1303 return pl->find_next_region (pos, point, dir);
1306 return boost::shared_ptr<Region> ();
1310 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1312 boost::shared_ptr<Playlist> pl = playlist ();
1315 return pl->find_next_region_boundary (pos, dir);
1322 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1324 boost::shared_ptr<Playlist> what_we_got;
1325 boost::shared_ptr<Track> tr = track ();
1326 boost::shared_ptr<Playlist> playlist;
1329 /* route is a bus, not a track */
1333 playlist = tr->playlist();
1335 TimeSelection time (selection.time);
1336 float const speed = tr->speed();
1337 if (speed != 1.0f) {
1338 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1339 (*i).start = session_frame_to_track_frame((*i).start, speed);
1340 (*i).end = session_frame_to_track_frame((*i).end, speed);
1344 playlist->clear_changes ();
1345 playlist->clear_owned_changes ();
1349 if (playlist->cut (time) != 0) {
1350 vector<Command*> cmds;
1351 playlist->rdiff (cmds);
1352 _session->add_commands (cmds);
1354 _session->add_command (new StatefulDiffCommand (playlist));
1359 if ((what_we_got = playlist->cut (time)) != 0) {
1360 _editor.get_cut_buffer().add (what_we_got);
1361 vector<Command*> cmds;
1362 playlist->rdiff (cmds);
1363 _session->add_commands (cmds);
1365 _session->add_command (new StatefulDiffCommand (playlist));
1369 if ((what_we_got = playlist->copy (time)) != 0) {
1370 _editor.get_cut_buffer().add (what_we_got);
1375 if ((what_we_got = playlist->cut (time)) != 0) {
1377 vector<Command*> cmds;
1378 playlist->rdiff (cmds);
1379 _session->add_commands (cmds);
1380 _session->add_command (new StatefulDiffCommand (playlist));
1381 what_we_got->release ();
1388 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1394 boost::shared_ptr<Playlist> pl = playlist ();
1395 PlaylistSelection::iterator p;
1397 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1399 if (p == selection.playlists.end()) {
1403 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1405 if (track()->speed() != 1.0f) {
1406 pos = session_frame_to_track_frame (pos, track()->speed());
1407 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1410 pl->clear_changes ();
1411 pl->paste (*p, pos, times);
1412 _session->add_command (new StatefulDiffCommand (pl));
1418 struct PlaylistSorter {
1419 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1420 return a->sort_id() < b->sort_id();
1425 RouteTimeAxisView::build_playlist_menu ()
1427 using namespace Menu_Helpers;
1433 delete playlist_action_menu;
1434 playlist_action_menu = new Menu;
1435 playlist_action_menu->set_name ("ArdourContextMenu");
1437 MenuList& playlist_items = playlist_action_menu->items();
1438 playlist_action_menu->set_name ("ArdourContextMenu");
1439 playlist_items.clear();
1441 RadioMenuItem::Group playlist_group;
1442 boost::shared_ptr<Track> tr = track ();
1444 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1446 /* sort the playlists */
1448 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1450 /* add the playlists to the menu */
1451 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1452 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1453 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1454 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1456 if (tr->playlist()->id() == (*i)->id()) {
1462 playlist_items.push_back (SeparatorElem());
1463 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1464 playlist_items.push_back (SeparatorElem());
1466 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1467 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1468 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1471 // Use a label which tells the user what is happening
1472 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1473 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1477 playlist_items.push_back (SeparatorElem());
1478 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1479 playlist_items.push_back (SeparatorElem());
1481 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1485 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1487 assert (is_track());
1489 // exit if we were triggered by deactivating the old playlist
1490 if (!item->get_active()) {
1494 boost::shared_ptr<Playlist> pl (wpl.lock());
1500 if (track()->playlist() == pl) {
1501 // exit when use_playlist is called by the creation of the playlist menu
1502 // or the playlist choice is unchanged
1506 track()->use_playlist (pl);
1508 RouteGroup* rg = route_group();
1510 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1511 std::string group_string = "." + rg->name() + ".";
1513 std::string take_name = pl->name();
1514 std::string::size_type idx = take_name.find(group_string);
1516 if (idx == std::string::npos)
1519 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1521 boost::shared_ptr<RouteList> rl (rg->route_list());
1523 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1524 if ((*i) == this->route()) {
1528 std::string playlist_name = (*i)->name()+group_string+take_name;
1530 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1535 if (track->freeze_state() == Track::Frozen) {
1536 /* Don't change playlists of frozen tracks */
1540 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1542 // No playlist for this track for this take yet, make it
1543 track->use_new_playlist();
1544 track->playlist()->set_name(playlist_name);
1546 track->use_playlist(ipl);
1553 RouteTimeAxisView::update_playlist_tip ()
1555 RouteGroup* rg = route_group ();
1556 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1557 string group_string = "." + rg->name() + ".";
1559 string take_name = track()->playlist()->name();
1560 string::size_type idx = take_name.find(group_string);
1562 if (idx != string::npos) {
1563 /* find the bit containing the take number / name */
1564 take_name = take_name.substr (idx + group_string.length());
1566 /* set the playlist button tooltip to the take name */
1567 ARDOUR_UI::instance()->set_tip (
1569 string_compose(_("Take: %1.%2"),
1570 Glib::Markup::escape_text(rg->name()),
1571 Glib::Markup::escape_text(take_name))
1578 /* set the playlist button tooltip to the playlist name */
1579 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1584 RouteTimeAxisView::show_playlist_selector ()
1586 _editor.playlist_selector().show_for (this);
1590 RouteTimeAxisView::map_frozen ()
1596 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1598 switch (track()->freeze_state()) {
1600 playlist_button.set_sensitive (false);
1601 rec_enable_button->set_sensitive (false);
1604 playlist_button.set_sensitive (true);
1605 rec_enable_button->set_sensitive (true);
1611 RouteTimeAxisView::color_handler ()
1613 //case cTimeStretchOutline:
1614 if (timestretch_rect) {
1615 timestretch_rect->set_outline_color (ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get());
1617 //case cTimeStretchFill:
1618 if (timestretch_rect) {
1619 timestretch_rect->set_fill_color (ARDOUR_UI::config()->canvasvar_TimeStretchFill.get());
1625 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1626 * Will add track if necessary.
1629 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1631 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1632 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1635 /* it doesn't exist yet, so we don't care about the button state: just add it */
1636 create_automation_child (param, true);
1639 bool yn = menu->get_active();
1640 bool changed = false;
1642 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1644 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1645 will have done that for us.
1648 if (changed && !no_redraw) {
1656 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1658 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1664 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1666 if (menu && !_hidden) {
1667 ignore_toggle = true;
1668 menu->set_active (false);
1669 ignore_toggle = false;
1672 if (_route && !no_redraw) {
1679 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1681 if (apply_to_selection) {
1682 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1686 /* Show our automation */
1688 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1689 i->second->set_marked_for_display (true);
1691 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1694 menu->set_active(true);
1699 /* Show processor automation */
1701 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1702 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1703 if ((*ii)->view == 0) {
1704 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1707 (*ii)->menu_item->set_active (true);
1720 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1722 if (apply_to_selection) {
1723 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1727 /* Show our automation */
1729 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1730 if (i->second->has_automation()) {
1731 i->second->set_marked_for_display (true);
1733 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1735 menu->set_active(true);
1740 /* Show processor automation */
1742 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1743 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1744 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1745 (*ii)->menu_item->set_active (true);
1757 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1759 if (apply_to_selection) {
1760 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1764 /* Hide our automation */
1766 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1767 i->second->set_marked_for_display (false);
1769 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1772 menu->set_active (false);
1776 /* Hide processor automation */
1778 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1779 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1780 (*ii)->menu_item->set_active (false);
1791 RouteTimeAxisView::region_view_added (RegionView* rv)
1793 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1794 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1795 boost::shared_ptr<AutomationTimeAxisView> atv;
1797 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1802 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1803 (*i)->add_ghost(rv);
1807 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1809 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1815 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1817 parent.remove_processor_automation_node (this);
1821 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1824 remove_child (pan->view);
1828 RouteTimeAxisView::ProcessorAutomationNode*
1829 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1831 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1833 if ((*i)->processor == processor) {
1835 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1836 if ((*ii)->what == what) {
1846 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1848 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1851 ProcessorAutomationNode* pan;
1853 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1854 /* session state may never have been saved with new plugin */
1855 error << _("programming error: ")
1856 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1857 processor->name(), what.type(), (int) what.channel(), what.id() )
1867 boost::shared_ptr<AutomationControl> control
1868 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1870 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1871 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1872 _editor, *this, false, parent_canvas,
1873 processor->describe_parameter (what), processor->name()));
1875 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1877 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1880 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1885 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1888 pan->menu_item->set_active (false);
1897 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1899 boost::shared_ptr<Processor> processor (p.lock ());
1901 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1902 /* The Amp processor is a special case and is dealt with separately */
1906 set<Evoral::Parameter> existing;
1908 processor->what_has_data (existing);
1910 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1912 Evoral::Parameter param (*i);
1913 boost::shared_ptr<AutomationLine> al;
1915 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1918 add_processor_automation_curve (processor, param);
1924 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1926 using namespace Menu_Helpers;
1930 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1932 _automation_tracks[param] = track;
1934 /* existing state overrides "show" argument */
1935 string s = track->gui_property ("visible");
1937 show = string_is_affirmative (s);
1940 /* this might or might not change the visibility status, so don't rely on it */
1941 track->set_marked_for_display (show);
1943 if (show && !no_redraw) {
1947 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1948 /* MIDI-related parameters are always in the menu, there's no
1949 reason to rebuild the menu just because we added a automation
1950 lane for one of them. But if we add a non-MIDI automation
1951 lane, then we need to invalidate the display menu.
1953 delete display_menu;
1959 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1961 boost::shared_ptr<Processor> processor (p.lock ());
1963 if (!processor || !processor->display_to_user ()) {
1967 /* we use this override to veto the Amp processor from the plugin menu,
1968 as its automation lane can be accessed using the special "Fader" menu
1972 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1976 using namespace Menu_Helpers;
1977 ProcessorAutomationInfo *rai;
1978 list<ProcessorAutomationInfo*>::iterator x;
1980 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1982 if (automatable.empty()) {
1986 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1987 if ((*x)->processor == processor) {
1992 if (x == processor_automation.end()) {
1994 rai = new ProcessorAutomationInfo (processor);
1995 processor_automation.push_back (rai);
2003 /* any older menu was deleted at the top of processors_changed()
2004 when we cleared the subplugin menu.
2007 rai->menu = manage (new Menu);
2008 MenuList& items = rai->menu->items();
2009 rai->menu->set_name ("ArdourContextMenu");
2013 std::set<Evoral::Parameter> has_visible_automation;
2014 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2016 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2018 ProcessorAutomationNode* pan;
2019 CheckMenuItem* mitem;
2021 string name = processor->describe_parameter (*i);
2023 items.push_back (CheckMenuElem (name));
2024 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2026 _subplugin_menu_map[*i] = mitem;
2028 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2029 mitem->set_active(true);
2032 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2036 pan = new ProcessorAutomationNode (*i, mitem, *this);
2038 rai->lines.push_back (pan);
2042 pan->menu_item = mitem;
2046 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2049 /* add the menu for this processor, because the subplugin
2050 menu is always cleared at the top of processors_changed().
2051 this is the result of some poor design in gtkmm and/or
2055 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2060 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2061 RouteTimeAxisView::ProcessorAutomationNode* pan)
2063 bool showit = pan->menu_item->get_active();
2064 bool redraw = false;
2066 if (pan->view == 0 && showit) {
2067 add_processor_automation_curve (rai->processor, pan->what);
2071 if (pan->view && pan->view->set_marked_for_display (showit)) {
2075 if (redraw && !no_redraw) {
2081 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2083 if (c.type == RouteProcessorChange::MeterPointChange) {
2084 /* nothing to do if only the meter point has changed */
2088 using namespace Menu_Helpers;
2090 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2091 (*i)->valid = false;
2094 setup_processor_menu_and_curves ();
2096 bool deleted_processor_automation = false;
2098 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2100 list<ProcessorAutomationInfo*>::iterator tmp;
2108 processor_automation.erase (i);
2109 deleted_processor_automation = true;
2116 if (deleted_processor_automation && !no_redraw) {
2121 boost::shared_ptr<AutomationLine>
2122 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2124 ProcessorAutomationNode* pan;
2126 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2132 return boost::shared_ptr<AutomationLine>();
2136 RouteTimeAxisView::reset_processor_automation_curves ()
2138 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2144 RouteTimeAxisView::can_edit_name () const
2146 /* we do not allow track name changes if it is record enabled
2148 return !_route->record_enabled();
2152 RouteTimeAxisView::update_rec_display ()
2154 RouteUI::update_rec_display ();
2158 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2160 if (_ignore_set_layer_display) {
2164 if (apply_to_selection) {
2165 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2169 _view->set_layer_display (d);
2172 set_gui_property (X_("layer-display"), enum_2_string (d));
2177 RouteTimeAxisView::layer_display () const
2180 return _view->layer_display ();
2183 /* we don't know, since we don't have a _view, so just return something */
2189 boost::shared_ptr<AutomationTimeAxisView>
2190 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2192 AutomationTracks::iterator i = _automation_tracks.find(param);
2193 if (i != _automation_tracks.end()) {
2196 return boost::shared_ptr<AutomationTimeAxisView>();
2201 RouteTimeAxisView::fast_update ()
2203 gm.get_level_meter().update_meters ();
2207 RouteTimeAxisView::hide_meter ()
2210 gm.get_level_meter().hide_meters ();
2214 RouteTimeAxisView::show_meter ()
2220 RouteTimeAxisView::reset_meter ()
2222 if (Config->get_show_track_meters()) {
2223 gm.get_level_meter().setup_meters (height-5);
2230 RouteTimeAxisView::clear_meter ()
2232 gm.get_level_meter().clear_meters ();
2236 RouteTimeAxisView::meter_changed ()
2238 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2243 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2249 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2251 using namespace Menu_Helpers;
2253 if (!_underlay_streams.empty()) {
2254 MenuList& parent_items = parent_menu->items();
2255 Menu* gs_menu = manage (new Menu);
2256 gs_menu->set_name ("ArdourContextMenu");
2257 MenuList& gs_items = gs_menu->items();
2259 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2261 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2262 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2263 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2269 RouteTimeAxisView::set_underlay_state()
2271 if (!underlay_xml_node) {
2275 XMLNodeList nlist = underlay_xml_node->children();
2276 XMLNodeConstIterator niter;
2277 XMLNode *child_node;
2279 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2280 child_node = *niter;
2282 if (child_node->name() != "Underlay") {
2286 XMLProperty* prop = child_node->property ("id");
2288 PBD::ID id (prop->value());
2290 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2293 add_underlay(v->view(), false);
2302 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2308 RouteTimeAxisView& other = v->trackview();
2310 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2311 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2312 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2316 _underlay_streams.push_back(v);
2317 other._underlay_mirrors.push_back(this);
2319 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2321 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2323 if (!underlay_xml_node) {
2324 underlay_xml_node = xml_node->add_child("Underlays");
2327 XMLNode* node = underlay_xml_node->add_child("Underlay");
2328 XMLProperty* prop = node->add_property("id");
2329 prop->set_value(v->trackview().route()->id().to_s());
2336 RouteTimeAxisView::remove_underlay (StreamView* v)
2342 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2343 RouteTimeAxisView& other = v->trackview();
2345 if (it != _underlay_streams.end()) {
2346 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2348 if (gm == other._underlay_mirrors.end()) {
2349 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2353 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2355 _underlay_streams.erase(it);
2356 other._underlay_mirrors.erase(gm);
2358 if (underlay_xml_node) {
2359 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2365 RouteTimeAxisView::set_button_names ()
2367 if (_route && _route->solo_safe()) {
2368 solo_button->remove ();
2369 if (solo_safe_pixbuf == 0) {
2370 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2372 solo_button->set_image (solo_safe_pixbuf);
2373 solo_button->set_text (string());
2375 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2376 if (Config->get_solo_control_is_listen_control()) {
2377 switch (Config->get_listen_position()) {
2378 case AfterFaderListen:
2379 solo_button->set_text (_("A"));
2380 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2382 case PreFaderListen:
2383 solo_button->set_text (_("P"));
2384 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2388 solo_button->set_text (_("s"));
2389 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2392 mute_button->set_text (_("m"));
2396 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2398 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2399 if (i != _main_automation_menu_map.end()) {
2403 i = _subplugin_menu_map.find (param);
2404 if (i != _subplugin_menu_map.end()) {
2412 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2414 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2416 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2420 gain_track.reset (new AutomationTimeAxisView (_session,
2421 _route, _route->amp(), c, param,
2426 _route->amp()->describe_parameter(param)));
2429 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2432 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2436 void add_region_to_list (RegionView* rv, RegionList* l)
2438 l->push_back (rv->region());
2442 RouteTimeAxisView::combine_regions ()
2444 /* as of may 2011, we do not offer uncombine for MIDI tracks
2447 if (!is_audio_track()) {
2455 RegionList selected_regions;
2456 boost::shared_ptr<Playlist> playlist = track()->playlist();
2458 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2460 if (selected_regions.size() < 2) {
2464 playlist->clear_changes ();
2465 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2467 _session->add_command (new StatefulDiffCommand (playlist));
2468 /* make the new region be selected */
2470 return _view->find_view (compound_region);
2474 RouteTimeAxisView::uncombine_regions ()
2476 /* as of may 2011, we do not offer uncombine for MIDI tracks
2478 if (!is_audio_track()) {
2486 RegionList selected_regions;
2487 boost::shared_ptr<Playlist> playlist = track()->playlist();
2489 /* have to grab selected regions first because the uncombine is going
2490 * to change that in the middle of the list traverse
2493 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2495 playlist->clear_changes ();
2497 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2498 playlist->uncombine (*i);
2501 _session->add_command (new StatefulDiffCommand (playlist));
2505 RouteTimeAxisView::state_id() const
2507 return string_compose ("rtav %1", _route->id().to_s());
2512 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2514 TimeAxisView::remove_child (c);
2516 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2518 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2519 if (i->second == a) {
2520 _automation_tracks.erase (i);