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_samples_per_pixel));
236 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
238 PropertyList* plist = new PropertyList();
240 plist->add (ARDOUR::Properties::mute, true);
241 plist->add (ARDOUR::Properties::solo, true);
243 route_group_menu = new RouteGroupMenu (_session, plist);
245 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
247 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
250 RouteTimeAxisView::~RouteTimeAxisView ()
252 CatchDeletion (this);
254 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
258 delete playlist_action_menu;
259 playlist_action_menu = 0;
264 _automation_tracks.clear ();
266 delete route_group_menu;
270 RouteTimeAxisView::post_construct ()
272 /* map current state of the route */
274 update_diskstream_display ();
275 setup_processor_menu_and_curves ();
276 reset_processor_automation_curves ();
279 /** Set up the processor menu for the current set of processors, and
280 * display automation curves for any parameters which have data.
283 RouteTimeAxisView::setup_processor_menu_and_curves ()
285 _subplugin_menu_map.clear ();
286 subplugin_menu.items().clear ();
287 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
288 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
292 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
294 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
295 if (_route->route_group()) {
296 _route->route_group()->remove (_route);
302 r.push_back (route ());
304 route_group_menu->build (r);
305 route_group_menu->menu()->popup (ev->button, ev->time);
311 RouteTimeAxisView::playlist_changed ()
317 RouteTimeAxisView::label_view ()
319 string x = _route->name();
321 if (x != name_label.get_text()) {
322 name_label.set_text (x);
328 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
330 if (what_changed.contains (ARDOUR::Properties::name)) {
336 RouteTimeAxisView::take_name_changed (void *src)
344 RouteTimeAxisView::playlist_click ()
346 build_playlist_menu ();
347 conditionally_add_to_selection ();
348 playlist_action_menu->popup (1, gtk_get_current_event_time());
352 RouteTimeAxisView::automation_click ()
354 conditionally_add_to_selection ();
355 build_automation_action_menu (false);
356 automation_action_menu->popup (1, gtk_get_current_event_time());
360 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
362 using namespace Menu_Helpers;
364 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
365 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
368 detach_menu (subplugin_menu);
370 _main_automation_menu_map.clear ();
371 delete automation_action_menu;
372 automation_action_menu = new Menu;
374 MenuList& items = automation_action_menu->items();
376 automation_action_menu->set_name ("ArdourContextMenu");
378 items.push_back (MenuElem (_("Show All Automation"),
379 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
381 items.push_back (MenuElem (_("Show Existing Automation"),
382 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
384 items.push_back (MenuElem (_("Hide All Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
387 /* Attach the plugin submenu. It may have previously been used elsewhere,
388 so it was detached above
391 if (!subplugin_menu.items().empty()) {
392 items.push_back (SeparatorElem ());
393 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
394 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
399 RouteTimeAxisView::build_display_menu ()
401 using namespace Menu_Helpers;
405 TimeAxisView::build_display_menu ();
407 /* now fill it with our stuff */
409 MenuList& items = display_menu->items();
410 display_menu->set_name ("ArdourContextMenu");
412 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
415 detach_menu (*_size_menu);
418 items.push_back (MenuElem (_("Height"), *_size_menu));
420 items.push_back (SeparatorElem());
422 if (!Profile->get_sae()) {
423 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
424 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
425 items.push_back (SeparatorElem());
428 // Hook for derived classes to add type specific stuff
429 append_extra_display_menu_items ();
433 Menu* layers_menu = manage (new Menu);
434 MenuList &layers_items = layers_menu->items();
435 layers_menu->set_name("ArdourContextMenu");
437 RadioMenuItem::Group layers_group;
439 /* Find out how many overlaid/stacked tracks we have in the selection */
443 TrackSelection const & s = _editor.get_selection().tracks;
444 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
445 StreamView* v = (*i)->view ();
450 switch (v->layer_display ()) {
461 /* We're not connecting to signal_toggled() here; in the case where these two items are
462 set to be in the `inconsistent' state, it seems that one or other will end up active
463 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
464 select the active one, no toggled signal is emitted so nothing happens.
467 _ignore_set_layer_display = true;
469 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
470 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
471 i->set_active (overlaid != 0 && stacked == 0);
472 i->set_inconsistent (overlaid != 0 && stacked != 0);
473 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
475 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
476 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
477 i->set_active (overlaid == 0 && stacked != 0);
478 i->set_inconsistent (overlaid != 0 && stacked != 0);
479 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
481 _ignore_set_layer_display = false;
483 items.push_back (MenuElem (_("Layers"), *layers_menu));
485 if (!Profile->get_sae()) {
487 Menu* alignment_menu = manage (new Menu);
488 MenuList& alignment_items = alignment_menu->items();
489 alignment_menu->set_name ("ArdourContextMenu");
491 RadioMenuItem::Group align_group;
493 /* Same verbose hacks as for the layering options above */
499 boost::shared_ptr<Track> first_track;
501 TrackSelection const & s = _editor.get_selection().tracks;
502 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
503 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
504 if (!r || !r->is_track ()) {
509 first_track = r->track();
512 switch (r->track()->alignment_choice()) {
516 switch (r->track()->alignment_style()) {
517 case ExistingMaterial:
525 case UseExistingMaterial:
541 inconsistent = false;
550 if (!inconsistent && first_track) {
552 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
553 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
554 i->set_active (automatic != 0 && existing == 0 && capture == 0);
555 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
557 switch (first_track->alignment_choice()) {
559 switch (first_track->alignment_style()) {
560 case ExistingMaterial:
561 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
564 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
572 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
573 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
574 i->set_active (existing != 0 && capture == 0 && automatic == 0);
575 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
577 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
578 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
579 i->set_active (existing == 0 && capture != 0 && automatic == 0);
580 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
582 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
588 Menu* mode_menu = manage (new Menu);
589 MenuList& mode_items = mode_menu->items ();
590 mode_menu->set_name ("ArdourContextMenu");
592 RadioMenuItem::Group mode_group;
598 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
599 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
600 if (!r || !r->is_track ()) {
604 switch (r->track()->mode()) {
617 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
618 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
619 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
620 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
621 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
623 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
624 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
625 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
626 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
627 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
629 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
630 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
631 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
632 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
633 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
635 items.push_back (MenuElem (_("Mode"), *mode_menu));
639 items.push_back (SeparatorElem());
641 build_playlist_menu ();
642 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
643 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
646 route_group_menu->detach ();
649 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
650 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
652 r.push_back (rtv->route ());
657 r.push_back (route ());
660 route_group_menu->build (r);
661 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
663 build_automation_action_menu (true);
664 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
666 items.push_back (SeparatorElem());
670 TrackSelection const & s = _editor.get_selection().tracks;
671 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
672 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
677 if (r->route()->active()) {
684 items.push_back (CheckMenuElem (_("Active")));
685 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
686 bool click_sets_active = true;
687 if (active > 0 && inactive == 0) {
688 i->set_active (true);
689 click_sets_active = false;
690 } else if (active > 0 && inactive > 0) {
691 i->set_inconsistent (true);
693 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
695 items.push_back (SeparatorElem());
696 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
697 if (!Profile->get_sae()) {
698 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
700 items.push_front (SeparatorElem());
701 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
706 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
708 if (apply_to_selection) {
709 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
714 if (!track()->can_use_mode (mode, needs_bounce)) {
720 cerr << "would bounce this one\n";
725 track()->set_mode (mode);
727 rec_enable_button->remove ();
730 case ARDOUR::NonLayered:
732 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
733 rec_enable_button->set_text (string());
735 case ARDOUR::Destructive:
736 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
737 rec_enable_button->set_text (string());
741 rec_enable_button->show_all ();
746 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
748 TimeAxisView::show_timestretch (start, end, layers, layer);
758 /* check that the time selection was made in our route, or our route group.
759 remember that route_group() == 0 implies the route is *not* in a edit group.
762 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
763 /* this doesn't apply to us */
767 /* ignore it if our edit group is not active */
769 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
774 if (timestretch_rect == 0) {
775 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
776 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
777 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
780 timestretch_rect->show ();
781 timestretch_rect->raise_to_top ();
783 double const x1 = start / _editor.get_current_zoom();
784 double const x2 = (end - 1) / _editor.get_current_zoom();
786 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
787 x2, current_height() * (layers - layer) / layers));
791 RouteTimeAxisView::hide_timestretch ()
793 TimeAxisView::hide_timestretch ();
795 if (timestretch_rect) {
796 timestretch_rect->hide ();
801 RouteTimeAxisView::show_selection (TimeSelection& ts)
805 /* ignore it if our edit group is not active or if the selection was started
806 in some other track or route group (remember that route_group() == 0 means
807 that the track is not in an route group).
810 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
811 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
817 TimeAxisView::show_selection (ts);
821 RouteTimeAxisView::set_height (uint32_t h)
824 bool height_changed = (height == 0) || (h != height);
825 gm.get_level_meter().setup_meters (gmlen);
827 TimeAxisView::set_height (h);
830 _view->set_height ((double) current_height());
833 if (height >= preset_height (HeightNormal)) {
837 gm.get_gain_slider().show();
839 if (!_route || _route->is_monitor()) {
844 if (rec_enable_button)
845 rec_enable_button->show();
847 route_group_button.show();
848 automation_button.show();
850 if (is_track() && track()->mode() == ARDOUR::Normal) {
851 playlist_button.show();
858 gm.get_gain_slider().hide();
860 if (!_route || _route->is_monitor()) {
865 if (rec_enable_button)
866 rec_enable_button->show();
868 route_group_button.hide ();
869 automation_button.hide ();
871 if (is_track() && track()->mode() == ARDOUR::Normal) {
872 playlist_button.hide ();
877 if (height_changed && !no_redraw) {
878 /* only emit the signal if the height really changed */
884 RouteTimeAxisView::route_color_changed ()
887 _view->apply_color (color(), StreamView::RegionColor);
892 RouteTimeAxisView::reset_samples_per_pixel ()
894 set_samples_per_pixel (_editor.get_current_zoom());
898 RouteTimeAxisView::set_samples_per_pixel (double fpp)
903 speed = track()->speed();
907 _view->set_samples_per_pixel (fpp * speed);
910 TimeAxisView::set_samples_per_pixel (fpp * speed);
914 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
916 if (!mitem->get_active()) {
917 /* this is one of the two calls made when these radio menu items change status. this one
918 is for the item that became inactive, and we want to ignore it.
923 if (apply_to_selection) {
924 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
927 track()->set_align_choice (choice);
933 RouteTimeAxisView::rename_current_playlist ()
935 ArdourPrompter prompter (true);
938 boost::shared_ptr<Track> tr = track();
939 if (!tr || tr->destructive()) {
943 boost::shared_ptr<Playlist> pl = tr->playlist();
948 prompter.set_title (_("Rename Playlist"));
949 prompter.set_prompt (_("New name for playlist:"));
950 prompter.set_initial_text (pl->name());
951 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
952 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
954 switch (prompter.run ()) {
955 case Gtk::RESPONSE_ACCEPT:
956 prompter.get_result (name);
968 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
970 std::string ret (basename);
972 std::string const group_string = "." + route_group()->name() + ".";
974 // iterate through all playlists
976 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
977 std::string tmp = (*i)->name();
979 std::string::size_type idx = tmp.find(group_string);
980 // find those which belong to this group
981 if (idx != string::npos) {
982 tmp = tmp.substr(idx + group_string.length());
984 // and find the largest current number
995 snprintf (buf, sizeof(buf), "%d", maxnumber);
997 ret = this->name() + "." + route_group()->name () + "." + buf;
1003 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1007 boost::shared_ptr<Track> tr = track ();
1008 if (!tr || tr->destructive()) {
1012 boost::shared_ptr<const Playlist> pl = tr->playlist();
1019 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1020 name = resolve_new_group_playlist_name(name, playlists_before_op);
1023 while (_session->playlists->by_name(name)) {
1024 name = Playlist::bump_name (name, *_session);
1027 // TODO: The prompter "new" button should be de-activated if the user
1028 // specifies a playlist name which already exists in the session.
1032 ArdourPrompter prompter (true);
1034 prompter.set_title (_("New Copy Playlist"));
1035 prompter.set_prompt (_("Name for new playlist:"));
1036 prompter.set_initial_text (name);
1037 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1038 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1039 prompter.show_all ();
1041 switch (prompter.run ()) {
1042 case Gtk::RESPONSE_ACCEPT:
1043 prompter.get_result (name);
1051 if (name.length()) {
1052 tr->use_copy_playlist ();
1053 tr->playlist()->set_name (name);
1058 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1062 boost::shared_ptr<Track> tr = track ();
1063 if (!tr || tr->destructive()) {
1067 boost::shared_ptr<const Playlist> pl = tr->playlist();
1074 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1075 name = resolve_new_group_playlist_name(name,playlists_before_op);
1078 while (_session->playlists->by_name(name)) {
1079 name = Playlist::bump_name (name, *_session);
1085 ArdourPrompter prompter (true);
1087 prompter.set_title (_("New Playlist"));
1088 prompter.set_prompt (_("Name for new playlist:"));
1089 prompter.set_initial_text (name);
1090 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1091 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1093 switch (prompter.run ()) {
1094 case Gtk::RESPONSE_ACCEPT:
1095 prompter.get_result (name);
1103 if (name.length()) {
1104 tr->use_new_playlist ();
1105 tr->playlist()->set_name (name);
1110 RouteTimeAxisView::clear_playlist ()
1112 boost::shared_ptr<Track> tr = track ();
1113 if (!tr || tr->destructive()) {
1117 boost::shared_ptr<Playlist> pl = tr->playlist();
1122 _editor.clear_playlist (pl);
1126 RouteTimeAxisView::speed_changed ()
1128 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1132 RouteTimeAxisView::update_diskstream_display ()
1142 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1144 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1146 /* special case: select/deselect all tracks */
1147 if (_editor.get_selection().selected (this)) {
1148 _editor.get_selection().clear_tracks ();
1150 _editor.select_all_tracks ();
1156 switch (ArdourKeyboard::selection_type (ev->state)) {
1157 case Selection::Toggle:
1158 _editor.get_selection().toggle (this);
1161 case Selection::Set:
1162 _editor.get_selection().set (this);
1165 case Selection::Extend:
1166 _editor.extend_selection_to_track (*this);
1169 case Selection::Add:
1170 _editor.get_selection().add (this);
1176 RouteTimeAxisView::set_selected_points (PointSelection& points)
1178 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1179 (*i)->set_selected_points (points);
1184 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1187 _view->set_selected_regionviews (regions);
1191 /** Add the selectable things that we have to a list.
1192 * @param results List to add things to.
1195 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1200 speed = track()->speed();
1203 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1204 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1206 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1207 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1210 /* pick up visible automation tracks */
1212 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1213 if (!(*i)->hidden()) {
1214 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1220 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1223 _view->get_inverted_selectables (sel, results);
1226 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1227 if (!(*i)->hidden()) {
1228 (*i)->get_inverted_selectables (sel, results);
1236 RouteTimeAxisView::route_group () const
1238 return _route->route_group();
1242 RouteTimeAxisView::name() const
1244 return _route->name();
1247 boost::shared_ptr<Playlist>
1248 RouteTimeAxisView::playlist () const
1250 boost::shared_ptr<Track> tr;
1252 if ((tr = track()) != 0) {
1253 return tr->playlist();
1255 return boost::shared_ptr<Playlist> ();
1260 RouteTimeAxisView::name_entry_changed ()
1262 TimeAxisView::name_entry_changed ();
1264 string x = name_entry->get_text ();
1266 if (x == _route->name()) {
1270 strip_whitespace_edges (x);
1272 if (x.length() == 0) {
1273 name_entry->set_text (_route->name());
1277 if (_session->route_name_internal (x)) {
1278 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1280 name_entry->grab_focus ();
1281 } else if (RouteUI::verify_new_route_name (x)) {
1282 _route->set_name (x);
1284 name_entry->grab_focus ();
1288 boost::shared_ptr<Region>
1289 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1291 boost::shared_ptr<Playlist> pl = playlist ();
1294 return pl->find_next_region (pos, point, dir);
1297 return boost::shared_ptr<Region> ();
1301 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1303 boost::shared_ptr<Playlist> pl = playlist ();
1306 return pl->find_next_region_boundary (pos, dir);
1313 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1315 boost::shared_ptr<Playlist> what_we_got;
1316 boost::shared_ptr<Track> tr = track ();
1317 boost::shared_ptr<Playlist> playlist;
1320 /* route is a bus, not a track */
1324 playlist = tr->playlist();
1326 TimeSelection time (selection.time);
1327 float const speed = tr->speed();
1328 if (speed != 1.0f) {
1329 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1330 (*i).start = session_frame_to_track_frame((*i).start, speed);
1331 (*i).end = session_frame_to_track_frame((*i).end, speed);
1335 playlist->clear_changes ();
1336 playlist->clear_owned_changes ();
1340 if (playlist->cut (time) != 0) {
1341 vector<Command*> cmds;
1342 playlist->rdiff (cmds);
1343 _session->add_commands (cmds);
1345 _session->add_command (new StatefulDiffCommand (playlist));
1350 if ((what_we_got = playlist->cut (time)) != 0) {
1351 _editor.get_cut_buffer().add (what_we_got);
1352 vector<Command*> cmds;
1353 playlist->rdiff (cmds);
1354 _session->add_commands (cmds);
1356 _session->add_command (new StatefulDiffCommand (playlist));
1360 if ((what_we_got = playlist->copy (time)) != 0) {
1361 _editor.get_cut_buffer().add (what_we_got);
1366 if ((what_we_got = playlist->cut (time)) != 0) {
1368 vector<Command*> cmds;
1369 playlist->rdiff (cmds);
1370 _session->add_commands (cmds);
1371 _session->add_command (new StatefulDiffCommand (playlist));
1372 what_we_got->release ();
1379 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1385 boost::shared_ptr<Playlist> pl = playlist ();
1386 PlaylistSelection::iterator p;
1388 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1390 if (p == selection.playlists.end()) {
1394 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1396 if (track()->speed() != 1.0f) {
1397 pos = session_frame_to_track_frame (pos, track()->speed());
1398 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1401 pl->clear_changes ();
1402 pl->paste (*p, pos, times);
1403 _session->add_command (new StatefulDiffCommand (pl));
1409 struct PlaylistSorter {
1410 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1411 return a->sort_id() < b->sort_id();
1416 RouteTimeAxisView::build_playlist_menu ()
1418 using namespace Menu_Helpers;
1424 delete playlist_action_menu;
1425 playlist_action_menu = new Menu;
1426 playlist_action_menu->set_name ("ArdourContextMenu");
1428 MenuList& playlist_items = playlist_action_menu->items();
1429 playlist_action_menu->set_name ("ArdourContextMenu");
1430 playlist_items.clear();
1432 RadioMenuItem::Group playlist_group;
1433 boost::shared_ptr<Track> tr = track ();
1435 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1437 /* sort the playlists */
1439 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1441 /* add the playlists to the menu */
1442 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1443 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1444 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1445 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1447 if (tr->playlist()->id() == (*i)->id()) {
1453 playlist_items.push_back (SeparatorElem());
1454 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1455 playlist_items.push_back (SeparatorElem());
1457 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1458 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1459 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1462 // Use a label which tells the user what is happening
1463 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1464 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1468 playlist_items.push_back (SeparatorElem());
1469 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1470 playlist_items.push_back (SeparatorElem());
1472 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1476 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1478 assert (is_track());
1480 // exit if we were triggered by deactivating the old playlist
1481 if (!item->get_active()) {
1485 boost::shared_ptr<Playlist> pl (wpl.lock());
1491 if (track()->playlist() == pl) {
1492 // exit when use_playlist is called by the creation of the playlist menu
1493 // or the playlist choice is unchanged
1497 track()->use_playlist (pl);
1499 RouteGroup* rg = route_group();
1501 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1502 std::string group_string = "." + rg->name() + ".";
1504 std::string take_name = pl->name();
1505 std::string::size_type idx = take_name.find(group_string);
1507 if (idx == std::string::npos)
1510 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1512 boost::shared_ptr<RouteList> rl (rg->route_list());
1514 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1515 if ((*i) == this->route()) {
1519 std::string playlist_name = (*i)->name()+group_string+take_name;
1521 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1526 if (track->freeze_state() == Track::Frozen) {
1527 /* Don't change playlists of frozen tracks */
1531 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1533 // No playlist for this track for this take yet, make it
1534 track->use_new_playlist();
1535 track->playlist()->set_name(playlist_name);
1537 track->use_playlist(ipl);
1544 RouteTimeAxisView::update_playlist_tip ()
1546 RouteGroup* rg = route_group ();
1547 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1548 string group_string = "." + rg->name() + ".";
1550 string take_name = track()->playlist()->name();
1551 string::size_type idx = take_name.find(group_string);
1553 if (idx != string::npos) {
1554 /* find the bit containing the take number / name */
1555 take_name = take_name.substr (idx + group_string.length());
1557 /* set the playlist button tooltip to the take name */
1558 ARDOUR_UI::instance()->set_tip (
1560 string_compose(_("Take: %1.%2"),
1561 Glib::Markup::escape_text(rg->name()),
1562 Glib::Markup::escape_text(take_name))
1569 /* set the playlist button tooltip to the playlist name */
1570 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1575 RouteTimeAxisView::show_playlist_selector ()
1577 _editor.playlist_selector().show_for (this);
1581 RouteTimeAxisView::map_frozen ()
1587 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1589 switch (track()->freeze_state()) {
1591 playlist_button.set_sensitive (false);
1592 rec_enable_button->set_sensitive (false);
1595 playlist_button.set_sensitive (true);
1596 rec_enable_button->set_sensitive (true);
1602 RouteTimeAxisView::color_handler ()
1604 //case cTimeStretchOutline:
1605 if (timestretch_rect) {
1606 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1608 //case cTimeStretchFill:
1609 if (timestretch_rect) {
1610 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1616 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1617 * Will add track if necessary.
1620 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1622 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1623 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1626 /* it doesn't exist yet, so we don't care about the button state: just add it */
1627 create_automation_child (param, true);
1630 bool yn = menu->get_active();
1631 bool changed = false;
1633 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1635 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1636 will have done that for us.
1639 if (changed && !no_redraw) {
1647 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1649 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1655 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1657 if (menu && !_hidden) {
1658 ignore_toggle = true;
1659 menu->set_active (false);
1660 ignore_toggle = false;
1663 if (_route && !no_redraw) {
1670 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1672 if (apply_to_selection) {
1673 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1677 /* Show our automation */
1679 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1680 i->second->set_marked_for_display (true);
1682 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1685 menu->set_active(true);
1690 /* Show processor automation */
1692 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1693 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1694 if ((*ii)->view == 0) {
1695 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1698 (*ii)->menu_item->set_active (true);
1711 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1713 if (apply_to_selection) {
1714 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1718 /* Show our automation */
1720 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1721 if (i->second->has_automation()) {
1722 i->second->set_marked_for_display (true);
1724 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1726 menu->set_active(true);
1731 /* Show processor automation */
1733 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1734 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1735 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1736 (*ii)->menu_item->set_active (true);
1748 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1750 if (apply_to_selection) {
1751 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1755 /* Hide our automation */
1757 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1758 i->second->set_marked_for_display (false);
1760 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1763 menu->set_active (false);
1767 /* Hide processor automation */
1769 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1770 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1771 (*ii)->menu_item->set_active (false);
1782 RouteTimeAxisView::region_view_added (RegionView* rv)
1784 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1785 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1786 boost::shared_ptr<AutomationTimeAxisView> atv;
1788 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1793 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1794 (*i)->add_ghost(rv);
1798 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1800 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1806 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1808 parent.remove_processor_automation_node (this);
1812 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1815 remove_child (pan->view);
1819 RouteTimeAxisView::ProcessorAutomationNode*
1820 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1822 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1824 if ((*i)->processor == processor) {
1826 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1827 if ((*ii)->what == what) {
1837 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1839 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1842 ProcessorAutomationNode* pan;
1844 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1845 /* session state may never have been saved with new plugin */
1846 error << _("programming error: ")
1847 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1848 processor->name(), what.type(), (int) what.channel(), what.id() )
1858 boost::shared_ptr<AutomationControl> control
1859 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1861 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1862 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1863 _editor, *this, false, parent_canvas,
1864 processor->describe_parameter (what), processor->name()));
1866 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1868 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1871 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1876 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1879 pan->menu_item->set_active (false);
1888 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1890 boost::shared_ptr<Processor> processor (p.lock ());
1892 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1893 /* The Amp processor is a special case and is dealt with separately */
1897 set<Evoral::Parameter> existing;
1899 processor->what_has_data (existing);
1901 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1903 Evoral::Parameter param (*i);
1904 boost::shared_ptr<AutomationLine> al;
1906 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1909 add_processor_automation_curve (processor, param);
1915 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1917 using namespace Menu_Helpers;
1921 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1923 _automation_tracks[param] = track;
1925 /* existing state overrides "show" argument */
1926 string s = track->gui_property ("visible");
1928 show = string_is_affirmative (s);
1931 /* this might or might not change the visibility status, so don't rely on it */
1932 track->set_marked_for_display (show);
1934 if (show && !no_redraw) {
1938 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1939 /* MIDI-related parameters are always in the menu, there's no
1940 reason to rebuild the menu just because we added a automation
1941 lane for one of them. But if we add a non-MIDI automation
1942 lane, then we need to invalidate the display menu.
1944 delete display_menu;
1950 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1952 boost::shared_ptr<Processor> processor (p.lock ());
1954 if (!processor || !processor->display_to_user ()) {
1958 /* we use this override to veto the Amp processor from the plugin menu,
1959 as its automation lane can be accessed using the special "Fader" menu
1963 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1967 using namespace Menu_Helpers;
1968 ProcessorAutomationInfo *rai;
1969 list<ProcessorAutomationInfo*>::iterator x;
1971 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1973 if (automatable.empty()) {
1977 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1978 if ((*x)->processor == processor) {
1983 if (x == processor_automation.end()) {
1985 rai = new ProcessorAutomationInfo (processor);
1986 processor_automation.push_back (rai);
1994 /* any older menu was deleted at the top of processors_changed()
1995 when we cleared the subplugin menu.
1998 rai->menu = manage (new Menu);
1999 MenuList& items = rai->menu->items();
2000 rai->menu->set_name ("ArdourContextMenu");
2004 std::set<Evoral::Parameter> has_visible_automation;
2005 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2007 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2009 ProcessorAutomationNode* pan;
2010 CheckMenuItem* mitem;
2012 string name = processor->describe_parameter (*i);
2014 items.push_back (CheckMenuElem (name));
2015 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2017 _subplugin_menu_map[*i] = mitem;
2019 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2020 mitem->set_active(true);
2023 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2027 pan = new ProcessorAutomationNode (*i, mitem, *this);
2029 rai->lines.push_back (pan);
2033 pan->menu_item = mitem;
2037 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2040 /* add the menu for this processor, because the subplugin
2041 menu is always cleared at the top of processors_changed().
2042 this is the result of some poor design in gtkmm and/or
2046 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2051 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2052 RouteTimeAxisView::ProcessorAutomationNode* pan)
2054 bool showit = pan->menu_item->get_active();
2055 bool redraw = false;
2057 if (pan->view == 0 && showit) {
2058 add_processor_automation_curve (rai->processor, pan->what);
2062 if (pan->view && pan->view->set_marked_for_display (showit)) {
2066 if (redraw && !no_redraw) {
2072 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2074 if (c.type == RouteProcessorChange::MeterPointChange) {
2075 /* nothing to do if only the meter point has changed */
2079 using namespace Menu_Helpers;
2081 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2082 (*i)->valid = false;
2085 setup_processor_menu_and_curves ();
2087 bool deleted_processor_automation = false;
2089 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2091 list<ProcessorAutomationInfo*>::iterator tmp;
2099 processor_automation.erase (i);
2100 deleted_processor_automation = true;
2107 if (deleted_processor_automation && !no_redraw) {
2112 boost::shared_ptr<AutomationLine>
2113 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2115 ProcessorAutomationNode* pan;
2117 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2123 return boost::shared_ptr<AutomationLine>();
2127 RouteTimeAxisView::reset_processor_automation_curves ()
2129 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2135 RouteTimeAxisView::can_edit_name () const
2137 /* we do not allow track name changes if it is record enabled
2139 return !_route->record_enabled();
2143 RouteTimeAxisView::update_rec_display ()
2145 RouteUI::update_rec_display ();
2149 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2151 if (_ignore_set_layer_display) {
2155 if (apply_to_selection) {
2156 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2160 _view->set_layer_display (d);
2163 set_gui_property (X_("layer-display"), enum_2_string (d));
2168 RouteTimeAxisView::layer_display () const
2171 return _view->layer_display ();
2174 /* we don't know, since we don't have a _view, so just return something */
2180 boost::shared_ptr<AutomationTimeAxisView>
2181 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2183 AutomationTracks::iterator i = _automation_tracks.find(param);
2184 if (i != _automation_tracks.end()) {
2187 return boost::shared_ptr<AutomationTimeAxisView>();
2192 RouteTimeAxisView::fast_update ()
2194 gm.get_level_meter().update_meters ();
2198 RouteTimeAxisView::hide_meter ()
2201 gm.get_level_meter().hide_meters ();
2205 RouteTimeAxisView::show_meter ()
2211 RouteTimeAxisView::reset_meter ()
2213 if (Config->get_show_track_meters()) {
2214 gm.get_level_meter().setup_meters (height-5);
2221 RouteTimeAxisView::clear_meter ()
2223 gm.get_level_meter().clear_meters ();
2227 RouteTimeAxisView::meter_changed ()
2229 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2234 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2240 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2242 using namespace Menu_Helpers;
2244 if (!_underlay_streams.empty()) {
2245 MenuList& parent_items = parent_menu->items();
2246 Menu* gs_menu = manage (new Menu);
2247 gs_menu->set_name ("ArdourContextMenu");
2248 MenuList& gs_items = gs_menu->items();
2250 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2252 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2253 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2254 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2260 RouteTimeAxisView::set_underlay_state()
2262 if (!underlay_xml_node) {
2266 XMLNodeList nlist = underlay_xml_node->children();
2267 XMLNodeConstIterator niter;
2268 XMLNode *child_node;
2270 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2271 child_node = *niter;
2273 if (child_node->name() != "Underlay") {
2277 XMLProperty* prop = child_node->property ("id");
2279 PBD::ID id (prop->value());
2281 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2284 add_underlay(v->view(), false);
2293 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2299 RouteTimeAxisView& other = v->trackview();
2301 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2302 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2303 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2307 _underlay_streams.push_back(v);
2308 other._underlay_mirrors.push_back(this);
2310 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2312 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2314 if (!underlay_xml_node) {
2315 underlay_xml_node = xml_node->add_child("Underlays");
2318 XMLNode* node = underlay_xml_node->add_child("Underlay");
2319 XMLProperty* prop = node->add_property("id");
2320 prop->set_value(v->trackview().route()->id().to_s());
2327 RouteTimeAxisView::remove_underlay (StreamView* v)
2333 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2334 RouteTimeAxisView& other = v->trackview();
2336 if (it != _underlay_streams.end()) {
2337 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2339 if (gm == other._underlay_mirrors.end()) {
2340 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2344 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2346 _underlay_streams.erase(it);
2347 other._underlay_mirrors.erase(gm);
2349 if (underlay_xml_node) {
2350 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2356 RouteTimeAxisView::set_button_names ()
2358 if (_route && _route->solo_safe()) {
2359 solo_button->remove ();
2360 if (solo_safe_pixbuf == 0) {
2361 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2363 solo_button->set_image (solo_safe_pixbuf);
2364 solo_button->set_text (string());
2366 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2367 if (Config->get_solo_control_is_listen_control()) {
2368 switch (Config->get_listen_position()) {
2369 case AfterFaderListen:
2370 solo_button->set_text (_("A"));
2371 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2373 case PreFaderListen:
2374 solo_button->set_text (_("P"));
2375 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2379 solo_button->set_text (_("s"));
2380 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2383 mute_button->set_text (_("m"));
2387 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2389 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2390 if (i != _main_automation_menu_map.end()) {
2394 i = _subplugin_menu_map.find (param);
2395 if (i != _subplugin_menu_map.end()) {
2403 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2405 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2407 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2411 gain_track.reset (new AutomationTimeAxisView (_session,
2412 _route, _route->amp(), c, param,
2417 _route->amp()->describe_parameter(param)));
2420 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2423 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2427 void add_region_to_list (RegionView* rv, RegionList* l)
2429 l->push_back (rv->region());
2433 RouteTimeAxisView::combine_regions ()
2435 /* as of may 2011, we do not offer uncombine for MIDI tracks
2438 if (!is_audio_track()) {
2446 RegionList selected_regions;
2447 boost::shared_ptr<Playlist> playlist = track()->playlist();
2449 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2451 if (selected_regions.size() < 2) {
2455 playlist->clear_changes ();
2456 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2458 _session->add_command (new StatefulDiffCommand (playlist));
2459 /* make the new region be selected */
2461 return _view->find_view (compound_region);
2465 RouteTimeAxisView::uncombine_regions ()
2467 /* as of may 2011, we do not offer uncombine for MIDI tracks
2469 if (!is_audio_track()) {
2477 RegionList selected_regions;
2478 boost::shared_ptr<Playlist> playlist = track()->playlist();
2480 /* have to grab selected regions first because the uncombine is going
2481 * to change that in the middle of the list traverse
2484 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2486 playlist->clear_changes ();
2488 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2489 playlist->uncombine (*i);
2492 _session->add_command (new StatefulDiffCommand (playlist));
2496 RouteTimeAxisView::state_id() const
2498 return string_compose ("rtav %1", _route->id().to_s());
2503 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2505 TimeAxisView::remove_child (c);
2507 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2509 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2510 if (i->second == a) {
2511 _automation_tracks.erase (i);