2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
53 #include "evoral/Parameter.hpp"
55 #include "ardour_ui.h"
56 #include "ardour_button.h"
58 #include "global_signals.h"
59 #include "route_time_axis.h"
60 #include "automation_time_axis.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "streamview.h"
73 #include "route_group_menu.h"
75 #include "ardour/track.h"
79 using namespace ARDOUR;
81 using namespace Gtkmm2ext;
83 using namespace Editing;
87 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
90 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
92 , parent_canvas (canvas)
95 , route_group_button (_("g"))
96 , playlist_button (_("p"))
97 , automation_button (_("a"))
98 , automation_action_menu (0)
99 , plugins_submenu_item (0)
100 , route_group_menu (0)
101 , playlist_action_menu (0)
103 , color_mode_menu (0)
104 , gm (sess, true, 125, 18)
105 , _ignore_set_layer_display (false)
110 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
112 RouteUI::set_route (rt);
115 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
118 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
119 gm.get_level_meter().set_no_show_all();
120 gm.get_level_meter().setup_meters(50, meter_width);
121 gm.update_gain_sensitive ();
123 string str = gui_property ("height");
125 set_height (atoi (str));
127 set_height (preset_height (HeightNormal));
130 if (!_route->is_auditioner()) {
131 if (gui_property ("visible").empty()) {
132 set_gui_property ("visible", true);
135 set_gui_property ("visible", false);
139 update_solo_display ();
141 timestretch_rect = 0;
144 ignore_toggle = false;
146 route_group_button.set_name ("route button");
147 playlist_button.set_name ("route button");
148 automation_button.set_name ("route button");
150 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
151 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
152 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
158 switch (track()->mode()) {
160 case ARDOUR::NonLayered:
161 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
163 case ARDOUR::Destructive:
164 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
168 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
170 if (is_midi_track()) {
171 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
172 gm.set_fader_name ("MidiTrackFader");
174 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
175 gm.set_fader_name ("AudioTrackFader");
178 rec_enable_button->set_sensitive (_session->writable());
180 /* set playlist button tip to the current playlist, and make it update when it changes */
181 update_playlist_tip ();
182 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
185 gm.set_fader_name ("AudioBusFader");
188 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
189 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
190 controls_hbox.pack_start(*mtrbox, false, false, 4);
193 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
194 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
195 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
197 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
199 if (!_route->is_master()) {
200 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
203 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
204 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
206 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
207 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
208 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
210 if (is_midi_track()) {
211 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
213 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
218 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
220 if (is_track() && track()->mode() == ARDOUR::Normal) {
221 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
226 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
227 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
231 str = gui_property ("layer-display");
233 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
236 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
237 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
239 /* pick up the correct freeze state */
244 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
245 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
247 PropertyList* plist = new PropertyList();
249 plist->add (ARDOUR::Properties::mute, true);
250 plist->add (ARDOUR::Properties::solo, true);
252 route_group_menu = new RouteGroupMenu (_session, plist);
254 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
256 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
259 RouteTimeAxisView::~RouteTimeAxisView ()
261 CatchDeletion (this);
263 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
267 delete playlist_action_menu;
268 playlist_action_menu = 0;
273 _automation_tracks.clear ();
275 delete route_group_menu;
279 RouteTimeAxisView::post_construct ()
281 /* map current state of the route */
283 update_diskstream_display ();
284 setup_processor_menu_and_curves ();
285 reset_processor_automation_curves ();
288 /** Set up the processor menu for the current set of processors, and
289 * display automation curves for any parameters which have data.
292 RouteTimeAxisView::setup_processor_menu_and_curves ()
294 _subplugin_menu_map.clear ();
295 subplugin_menu.items().clear ();
296 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
297 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
301 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
303 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
304 if (_route->route_group()) {
305 _route->route_group()->remove (_route);
311 r.push_back (route ());
313 route_group_menu->build (r);
314 route_group_menu->menu()->popup (ev->button, ev->time);
320 RouteTimeAxisView::playlist_changed ()
326 RouteTimeAxisView::label_view ()
328 string x = _route->name();
330 if (x != name_label.get_text()) {
331 name_label.set_text (x);
337 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
339 if (what_changed.contains (ARDOUR::Properties::name)) {
345 RouteTimeAxisView::take_name_changed (void *src)
353 RouteTimeAxisView::playlist_click ()
355 build_playlist_menu ();
356 conditionally_add_to_selection ();
357 playlist_action_menu->popup (1, gtk_get_current_event_time());
361 RouteTimeAxisView::automation_click ()
363 conditionally_add_to_selection ();
364 build_automation_action_menu (false);
365 automation_action_menu->popup (1, gtk_get_current_event_time());
369 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
371 using namespace Menu_Helpers;
373 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
374 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
377 detach_menu (subplugin_menu);
379 _main_automation_menu_map.clear ();
380 delete automation_action_menu;
381 automation_action_menu = new Menu;
383 MenuList& items = automation_action_menu->items();
385 automation_action_menu->set_name ("ArdourContextMenu");
387 items.push_back (MenuElem (_("Show All Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
390 items.push_back (MenuElem (_("Show Existing Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
393 items.push_back (MenuElem (_("Hide All Automation"),
394 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
396 /* Attach the plugin submenu. It may have previously been used elsewhere,
397 so it was detached above
400 if (!subplugin_menu.items().empty()) {
401 items.push_back (SeparatorElem ());
402 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
403 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
408 RouteTimeAxisView::build_display_menu ()
410 using namespace Menu_Helpers;
414 TimeAxisView::build_display_menu ();
416 /* now fill it with our stuff */
418 MenuList& items = display_menu->items();
419 display_menu->set_name ("ArdourContextMenu");
421 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
424 detach_menu (*_size_menu);
427 items.push_back (MenuElem (_("Height"), *_size_menu));
429 items.push_back (SeparatorElem());
431 if (!Profile->get_sae()) {
432 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
433 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
434 items.push_back (SeparatorElem());
437 // Hook for derived classes to add type specific stuff
438 append_extra_display_menu_items ();
442 Menu* layers_menu = manage (new Menu);
443 MenuList &layers_items = layers_menu->items();
444 layers_menu->set_name("ArdourContextMenu");
446 RadioMenuItem::Group layers_group;
448 /* Find out how many overlaid/stacked tracks we have in the selection */
452 TrackSelection const & s = _editor.get_selection().tracks;
453 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
454 StreamView* v = (*i)->view ();
459 switch (v->layer_display ()) {
470 /* We're not connecting to signal_toggled() here; in the case where these two items are
471 set to be in the `inconsistent' state, it seems that one or other will end up active
472 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
473 select the active one, no toggled signal is emitted so nothing happens.
476 _ignore_set_layer_display = true;
478 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
479 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
480 i->set_active (overlaid != 0 && stacked == 0);
481 i->set_inconsistent (overlaid != 0 && stacked != 0);
482 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
484 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
485 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
486 i->set_active (overlaid == 0 && stacked != 0);
487 i->set_inconsistent (overlaid != 0 && stacked != 0);
488 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
490 _ignore_set_layer_display = false;
492 items.push_back (MenuElem (_("Layers"), *layers_menu));
494 if (!Profile->get_sae()) {
496 Menu* alignment_menu = manage (new Menu);
497 MenuList& alignment_items = alignment_menu->items();
498 alignment_menu->set_name ("ArdourContextMenu");
500 RadioMenuItem::Group align_group;
502 /* Same verbose hacks as for the layering options above */
508 boost::shared_ptr<Track> first_track;
510 TrackSelection const & s = _editor.get_selection().tracks;
511 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
512 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
513 if (!r || !r->is_track ()) {
518 first_track = r->track();
521 switch (r->track()->alignment_choice()) {
525 switch (r->track()->alignment_style()) {
526 case ExistingMaterial:
534 case UseExistingMaterial:
550 inconsistent = false;
559 if (!inconsistent && first_track) {
561 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
562 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
563 i->set_active (automatic != 0 && existing == 0 && capture == 0);
564 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
566 switch (first_track->alignment_choice()) {
568 switch (first_track->alignment_style()) {
569 case ExistingMaterial:
570 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
573 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
581 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
582 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
583 i->set_active (existing != 0 && capture == 0 && automatic == 0);
584 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
586 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
587 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
588 i->set_active (existing == 0 && capture != 0 && automatic == 0);
589 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
591 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
597 Menu* mode_menu = manage (new Menu);
598 MenuList& mode_items = mode_menu->items ();
599 mode_menu->set_name ("ArdourContextMenu");
601 RadioMenuItem::Group mode_group;
607 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
608 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
609 if (!r || !r->is_track ()) {
613 switch (r->track()->mode()) {
626 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
627 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
628 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
629 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
630 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
632 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
633 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
634 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
635 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
636 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
638 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
641 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
642 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
644 items.push_back (MenuElem (_("Mode"), *mode_menu));
648 items.push_back (SeparatorElem());
650 build_playlist_menu ();
651 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
652 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
655 route_group_menu->detach ();
658 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
659 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
661 r.push_back (rtv->route ());
666 r.push_back (route ());
669 route_group_menu->build (r);
670 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
672 build_automation_action_menu (true);
673 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
675 items.push_back (SeparatorElem());
679 TrackSelection const & s = _editor.get_selection().tracks;
680 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
681 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
686 if (r->route()->active()) {
693 items.push_back (CheckMenuElem (_("Active")));
694 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
695 bool click_sets_active = true;
696 if (active > 0 && inactive == 0) {
697 i->set_active (true);
698 click_sets_active = false;
699 } else if (active > 0 && inactive > 0) {
700 i->set_inconsistent (true);
702 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
704 items.push_back (SeparatorElem());
705 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
706 if (!Profile->get_sae()) {
707 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
709 items.push_front (SeparatorElem());
710 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
715 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
717 if (apply_to_selection) {
718 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
723 if (!track()->can_use_mode (mode, needs_bounce)) {
729 cerr << "would bounce this one\n";
734 track()->set_mode (mode);
736 rec_enable_button->remove ();
739 case ARDOUR::NonLayered:
741 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
742 rec_enable_button->set_text (string());
744 case ARDOUR::Destructive:
745 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
746 rec_enable_button->set_text (string());
750 rec_enable_button->show_all ();
755 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
757 TimeAxisView::show_timestretch (start, end, layers, layer);
767 /* check that the time selection was made in our route, or our route group.
768 remember that route_group() == 0 implies the route is *not* in a edit group.
771 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
772 /* this doesn't apply to us */
776 /* ignore it if our edit group is not active */
778 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
783 if (timestretch_rect == 0) {
784 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
785 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
786 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
789 timestretch_rect->show ();
790 timestretch_rect->raise_to_top ();
792 double const x1 = start / _editor.get_current_zoom();
793 double const x2 = (end - 1) / _editor.get_current_zoom();
795 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
796 x2, current_height() * (layers - layer) / layers));
800 RouteTimeAxisView::hide_timestretch ()
802 TimeAxisView::hide_timestretch ();
804 if (timestretch_rect) {
805 timestretch_rect->hide ();
810 RouteTimeAxisView::show_selection (TimeSelection& ts)
814 /* ignore it if our edit group is not active or if the selection was started
815 in some other track or route group (remember that route_group() == 0 means
816 that the track is not in an route group).
819 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
820 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
826 TimeAxisView::show_selection (ts);
830 RouteTimeAxisView::set_height (uint32_t h)
833 bool height_changed = (height == 0) || (h != height);
836 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
839 gm.get_level_meter().setup_meters (gmlen, meter_width);
841 TimeAxisView::set_height (h);
844 _view->set_height ((double) current_height());
847 if (height >= preset_height (HeightNormal)) {
851 gm.get_gain_slider().show();
853 if (!_route || _route->is_monitor()) {
858 if (rec_enable_button)
859 rec_enable_button->show();
861 route_group_button.show();
862 automation_button.show();
864 if (is_track() && track()->mode() == ARDOUR::Normal) {
865 playlist_button.show();
872 gm.get_gain_slider().hide();
874 if (!_route || _route->is_monitor()) {
879 if (rec_enable_button)
880 rec_enable_button->show();
882 route_group_button.hide ();
883 automation_button.hide ();
885 if (is_track() && track()->mode() == ARDOUR::Normal) {
886 playlist_button.hide ();
891 if (height_changed && !no_redraw) {
892 /* only emit the signal if the height really changed */
898 RouteTimeAxisView::route_color_changed ()
901 _view->apply_color (color(), StreamView::RegionColor);
906 RouteTimeAxisView::reset_samples_per_pixel ()
908 set_samples_per_pixel (_editor.get_current_zoom());
912 RouteTimeAxisView::set_samples_per_pixel (double fpp)
917 speed = track()->speed();
921 _view->set_samples_per_pixel (fpp * speed);
924 TimeAxisView::set_samples_per_pixel (fpp * speed);
928 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
930 if (!mitem->get_active()) {
931 /* this is one of the two calls made when these radio menu items change status. this one
932 is for the item that became inactive, and we want to ignore it.
937 if (apply_to_selection) {
938 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
941 track()->set_align_choice (choice);
947 RouteTimeAxisView::rename_current_playlist ()
949 ArdourPrompter prompter (true);
952 boost::shared_ptr<Track> tr = track();
953 if (!tr || tr->destructive()) {
957 boost::shared_ptr<Playlist> pl = tr->playlist();
962 prompter.set_title (_("Rename Playlist"));
963 prompter.set_prompt (_("New name for playlist:"));
964 prompter.set_initial_text (pl->name());
965 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
966 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
968 switch (prompter.run ()) {
969 case Gtk::RESPONSE_ACCEPT:
970 prompter.get_result (name);
982 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
984 std::string ret (basename);
986 std::string const group_string = "." + route_group()->name() + ".";
988 // iterate through all playlists
990 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
991 std::string tmp = (*i)->name();
993 std::string::size_type idx = tmp.find(group_string);
994 // find those which belong to this group
995 if (idx != string::npos) {
996 tmp = tmp.substr(idx + group_string.length());
998 // and find the largest current number
1000 if (x > maxnumber) {
1009 snprintf (buf, sizeof(buf), "%d", maxnumber);
1011 ret = this->name() + "." + route_group()->name () + "." + buf;
1017 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1021 boost::shared_ptr<Track> tr = track ();
1022 if (!tr || tr->destructive()) {
1026 boost::shared_ptr<const Playlist> pl = tr->playlist();
1033 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1034 name = resolve_new_group_playlist_name(name, playlists_before_op);
1037 while (_session->playlists->by_name(name)) {
1038 name = Playlist::bump_name (name, *_session);
1041 // TODO: The prompter "new" button should be de-activated if the user
1042 // specifies a playlist name which already exists in the session.
1046 ArdourPrompter prompter (true);
1048 prompter.set_title (_("New Copy Playlist"));
1049 prompter.set_prompt (_("Name for new playlist:"));
1050 prompter.set_initial_text (name);
1051 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1052 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1053 prompter.show_all ();
1055 switch (prompter.run ()) {
1056 case Gtk::RESPONSE_ACCEPT:
1057 prompter.get_result (name);
1065 if (name.length()) {
1066 tr->use_copy_playlist ();
1067 tr->playlist()->set_name (name);
1072 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1076 boost::shared_ptr<Track> tr = track ();
1077 if (!tr || tr->destructive()) {
1081 boost::shared_ptr<const Playlist> pl = tr->playlist();
1088 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1089 name = resolve_new_group_playlist_name(name,playlists_before_op);
1092 while (_session->playlists->by_name(name)) {
1093 name = Playlist::bump_name (name, *_session);
1099 ArdourPrompter prompter (true);
1101 prompter.set_title (_("New Playlist"));
1102 prompter.set_prompt (_("Name for new playlist:"));
1103 prompter.set_initial_text (name);
1104 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1105 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1107 switch (prompter.run ()) {
1108 case Gtk::RESPONSE_ACCEPT:
1109 prompter.get_result (name);
1117 if (name.length()) {
1118 tr->use_new_playlist ();
1119 tr->playlist()->set_name (name);
1124 RouteTimeAxisView::clear_playlist ()
1126 boost::shared_ptr<Track> tr = track ();
1127 if (!tr || tr->destructive()) {
1131 boost::shared_ptr<Playlist> pl = tr->playlist();
1136 _editor.clear_playlist (pl);
1140 RouteTimeAxisView::speed_changed ()
1142 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1146 RouteTimeAxisView::update_diskstream_display ()
1156 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1158 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1160 /* special case: select/deselect all tracks */
1161 if (_editor.get_selection().selected (this)) {
1162 _editor.get_selection().clear_tracks ();
1164 _editor.select_all_tracks ();
1170 switch (ArdourKeyboard::selection_type (ev->state)) {
1171 case Selection::Toggle:
1172 _editor.get_selection().toggle (this);
1175 case Selection::Set:
1176 _editor.get_selection().set (this);
1179 case Selection::Extend:
1180 _editor.extend_selection_to_track (*this);
1183 case Selection::Add:
1184 _editor.get_selection().add (this);
1190 RouteTimeAxisView::set_selected_points (PointSelection& points)
1192 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1193 (*i)->set_selected_points (points);
1198 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1201 _view->set_selected_regionviews (regions);
1205 /** Add the selectable things that we have to a list.
1206 * @param results List to add things to.
1209 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1214 speed = track()->speed();
1217 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1218 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1220 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1221 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1224 /* pick up visible automation tracks */
1226 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1227 if (!(*i)->hidden()) {
1228 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1234 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1237 _view->get_inverted_selectables (sel, results);
1240 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1241 if (!(*i)->hidden()) {
1242 (*i)->get_inverted_selectables (sel, results);
1250 RouteTimeAxisView::route_group () const
1252 return _route->route_group();
1256 RouteTimeAxisView::name() const
1258 return _route->name();
1261 boost::shared_ptr<Playlist>
1262 RouteTimeAxisView::playlist () const
1264 boost::shared_ptr<Track> tr;
1266 if ((tr = track()) != 0) {
1267 return tr->playlist();
1269 return boost::shared_ptr<Playlist> ();
1274 RouteTimeAxisView::name_entry_changed ()
1276 TimeAxisView::name_entry_changed ();
1278 string x = name_entry->get_text ();
1280 if (x == _route->name()) {
1284 strip_whitespace_edges (x);
1286 if (x.length() == 0) {
1287 name_entry->set_text (_route->name());
1291 if (_session->route_name_internal (x)) {
1292 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1294 name_entry->grab_focus ();
1295 } else if (RouteUI::verify_new_route_name (x)) {
1296 _route->set_name (x);
1298 name_entry->grab_focus ();
1302 boost::shared_ptr<Region>
1303 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1305 boost::shared_ptr<Playlist> pl = playlist ();
1308 return pl->find_next_region (pos, point, dir);
1311 return boost::shared_ptr<Region> ();
1315 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1317 boost::shared_ptr<Playlist> pl = playlist ();
1320 return pl->find_next_region_boundary (pos, dir);
1327 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1329 boost::shared_ptr<Playlist> what_we_got;
1330 boost::shared_ptr<Track> tr = track ();
1331 boost::shared_ptr<Playlist> playlist;
1334 /* route is a bus, not a track */
1338 playlist = tr->playlist();
1340 TimeSelection time (selection.time);
1341 float const speed = tr->speed();
1342 if (speed != 1.0f) {
1343 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1344 (*i).start = session_frame_to_track_frame((*i).start, speed);
1345 (*i).end = session_frame_to_track_frame((*i).end, speed);
1349 playlist->clear_changes ();
1350 playlist->clear_owned_changes ();
1354 if (playlist->cut (time) != 0) {
1355 vector<Command*> cmds;
1356 playlist->rdiff (cmds);
1357 _session->add_commands (cmds);
1359 _session->add_command (new StatefulDiffCommand (playlist));
1364 if ((what_we_got = playlist->cut (time)) != 0) {
1365 _editor.get_cut_buffer().add (what_we_got);
1366 vector<Command*> cmds;
1367 playlist->rdiff (cmds);
1368 _session->add_commands (cmds);
1370 _session->add_command (new StatefulDiffCommand (playlist));
1374 if ((what_we_got = playlist->copy (time)) != 0) {
1375 _editor.get_cut_buffer().add (what_we_got);
1380 if ((what_we_got = playlist->cut (time)) != 0) {
1382 vector<Command*> cmds;
1383 playlist->rdiff (cmds);
1384 _session->add_commands (cmds);
1385 _session->add_command (new StatefulDiffCommand (playlist));
1386 what_we_got->release ();
1393 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1399 boost::shared_ptr<Playlist> pl = playlist ();
1400 PlaylistSelection::iterator p;
1402 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1404 if (p == selection.playlists.end()) {
1408 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1410 if (track()->speed() != 1.0f) {
1411 pos = session_frame_to_track_frame (pos, track()->speed());
1412 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1415 pl->clear_changes ();
1416 pl->paste (*p, pos, times);
1417 _session->add_command (new StatefulDiffCommand (pl));
1423 struct PlaylistSorter {
1424 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1425 return a->sort_id() < b->sort_id();
1430 RouteTimeAxisView::build_playlist_menu ()
1432 using namespace Menu_Helpers;
1438 delete playlist_action_menu;
1439 playlist_action_menu = new Menu;
1440 playlist_action_menu->set_name ("ArdourContextMenu");
1442 MenuList& playlist_items = playlist_action_menu->items();
1443 playlist_action_menu->set_name ("ArdourContextMenu");
1444 playlist_items.clear();
1446 RadioMenuItem::Group playlist_group;
1447 boost::shared_ptr<Track> tr = track ();
1449 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1451 /* sort the playlists */
1453 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1455 /* add the playlists to the menu */
1456 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1457 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1458 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1459 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1461 if (tr->playlist()->id() == (*i)->id()) {
1467 playlist_items.push_back (SeparatorElem());
1468 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1469 playlist_items.push_back (SeparatorElem());
1471 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1472 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1473 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1476 // Use a label which tells the user what is happening
1477 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1478 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1482 playlist_items.push_back (SeparatorElem());
1483 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1484 playlist_items.push_back (SeparatorElem());
1486 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1490 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1492 assert (is_track());
1494 // exit if we were triggered by deactivating the old playlist
1495 if (!item->get_active()) {
1499 boost::shared_ptr<Playlist> pl (wpl.lock());
1505 if (track()->playlist() == pl) {
1506 // exit when use_playlist is called by the creation of the playlist menu
1507 // or the playlist choice is unchanged
1511 track()->use_playlist (pl);
1513 RouteGroup* rg = route_group();
1515 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1516 std::string group_string = "." + rg->name() + ".";
1518 std::string take_name = pl->name();
1519 std::string::size_type idx = take_name.find(group_string);
1521 if (idx == std::string::npos)
1524 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1526 boost::shared_ptr<RouteList> rl (rg->route_list());
1528 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1529 if ((*i) == this->route()) {
1533 std::string playlist_name = (*i)->name()+group_string+take_name;
1535 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1540 if (track->freeze_state() == Track::Frozen) {
1541 /* Don't change playlists of frozen tracks */
1545 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1547 // No playlist for this track for this take yet, make it
1548 track->use_new_playlist();
1549 track->playlist()->set_name(playlist_name);
1551 track->use_playlist(ipl);
1558 RouteTimeAxisView::update_playlist_tip ()
1560 RouteGroup* rg = route_group ();
1561 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1562 string group_string = "." + rg->name() + ".";
1564 string take_name = track()->playlist()->name();
1565 string::size_type idx = take_name.find(group_string);
1567 if (idx != string::npos) {
1568 /* find the bit containing the take number / name */
1569 take_name = take_name.substr (idx + group_string.length());
1571 /* set the playlist button tooltip to the take name */
1572 ARDOUR_UI::instance()->set_tip (
1574 string_compose(_("Take: %1.%2"),
1575 Glib::Markup::escape_text(rg->name()),
1576 Glib::Markup::escape_text(take_name))
1583 /* set the playlist button tooltip to the playlist name */
1584 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1589 RouteTimeAxisView::show_playlist_selector ()
1591 _editor.playlist_selector().show_for (this);
1595 RouteTimeAxisView::map_frozen ()
1601 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1603 switch (track()->freeze_state()) {
1605 playlist_button.set_sensitive (false);
1606 rec_enable_button->set_sensitive (false);
1609 playlist_button.set_sensitive (true);
1610 rec_enable_button->set_sensitive (true);
1616 RouteTimeAxisView::color_handler ()
1618 //case cTimeStretchOutline:
1619 if (timestretch_rect) {
1620 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1622 //case cTimeStretchFill:
1623 if (timestretch_rect) {
1624 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1630 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1631 * Will add track if necessary.
1634 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1636 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1637 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1640 /* it doesn't exist yet, so we don't care about the button state: just add it */
1641 create_automation_child (param, true);
1644 bool yn = menu->get_active();
1645 bool changed = false;
1647 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1649 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1650 will have done that for us.
1653 if (changed && !no_redraw) {
1661 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1663 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1669 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1671 if (menu && !_hidden) {
1672 ignore_toggle = true;
1673 menu->set_active (false);
1674 ignore_toggle = false;
1677 if (_route && !no_redraw) {
1684 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1686 if (apply_to_selection) {
1687 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1691 /* Show our automation */
1693 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1694 i->second->set_marked_for_display (true);
1696 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1699 menu->set_active(true);
1704 /* Show processor automation */
1706 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1707 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1708 if ((*ii)->view == 0) {
1709 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1712 (*ii)->menu_item->set_active (true);
1725 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1727 if (apply_to_selection) {
1728 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1732 /* Show our automation */
1734 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1735 if (i->second->has_automation()) {
1736 i->second->set_marked_for_display (true);
1738 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1740 menu->set_active(true);
1745 /* Show processor automation */
1747 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1748 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1749 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1750 (*ii)->menu_item->set_active (true);
1762 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1764 if (apply_to_selection) {
1765 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1769 /* Hide our automation */
1771 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1772 i->second->set_marked_for_display (false);
1774 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1777 menu->set_active (false);
1781 /* Hide processor automation */
1783 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1784 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1785 (*ii)->menu_item->set_active (false);
1796 RouteTimeAxisView::region_view_added (RegionView* rv)
1798 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1799 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1800 boost::shared_ptr<AutomationTimeAxisView> atv;
1802 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1807 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1808 (*i)->add_ghost(rv);
1812 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1814 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1820 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1822 parent.remove_processor_automation_node (this);
1826 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1829 remove_child (pan->view);
1833 RouteTimeAxisView::ProcessorAutomationNode*
1834 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1836 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1838 if ((*i)->processor == processor) {
1840 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1841 if ((*ii)->what == what) {
1851 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1853 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1856 ProcessorAutomationNode* pan;
1858 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1859 /* session state may never have been saved with new plugin */
1860 error << _("programming error: ")
1861 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1862 processor->name(), what.type(), (int) what.channel(), what.id() )
1872 boost::shared_ptr<AutomationControl> control
1873 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1875 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1876 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1877 _editor, *this, false, parent_canvas,
1878 processor->describe_parameter (what), processor->name()));
1880 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1882 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1885 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1890 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1893 pan->menu_item->set_active (false);
1902 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1904 boost::shared_ptr<Processor> processor (p.lock ());
1906 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1907 /* The Amp processor is a special case and is dealt with separately */
1911 set<Evoral::Parameter> existing;
1913 processor->what_has_data (existing);
1915 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1917 Evoral::Parameter param (*i);
1918 boost::shared_ptr<AutomationLine> al;
1920 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1923 add_processor_automation_curve (processor, param);
1929 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1931 using namespace Menu_Helpers;
1935 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1937 _automation_tracks[param] = track;
1939 /* existing state overrides "show" argument */
1940 string s = track->gui_property ("visible");
1942 show = string_is_affirmative (s);
1945 /* this might or might not change the visibility status, so don't rely on it */
1946 track->set_marked_for_display (show);
1948 if (show && !no_redraw) {
1952 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1953 /* MIDI-related parameters are always in the menu, there's no
1954 reason to rebuild the menu just because we added a automation
1955 lane for one of them. But if we add a non-MIDI automation
1956 lane, then we need to invalidate the display menu.
1958 delete display_menu;
1964 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1966 boost::shared_ptr<Processor> processor (p.lock ());
1968 if (!processor || !processor->display_to_user ()) {
1972 /* we use this override to veto the Amp processor from the plugin menu,
1973 as its automation lane can be accessed using the special "Fader" menu
1977 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1981 using namespace Menu_Helpers;
1982 ProcessorAutomationInfo *rai;
1983 list<ProcessorAutomationInfo*>::iterator x;
1985 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1987 if (automatable.empty()) {
1991 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1992 if ((*x)->processor == processor) {
1997 if (x == processor_automation.end()) {
1999 rai = new ProcessorAutomationInfo (processor);
2000 processor_automation.push_back (rai);
2008 /* any older menu was deleted at the top of processors_changed()
2009 when we cleared the subplugin menu.
2012 rai->menu = manage (new Menu);
2013 MenuList& items = rai->menu->items();
2014 rai->menu->set_name ("ArdourContextMenu");
2018 std::set<Evoral::Parameter> has_visible_automation;
2019 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2021 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2023 ProcessorAutomationNode* pan;
2024 CheckMenuItem* mitem;
2026 string name = processor->describe_parameter (*i);
2028 items.push_back (CheckMenuElem (name));
2029 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2031 _subplugin_menu_map[*i] = mitem;
2033 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2034 mitem->set_active(true);
2037 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2041 pan = new ProcessorAutomationNode (*i, mitem, *this);
2043 rai->lines.push_back (pan);
2047 pan->menu_item = mitem;
2051 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2054 /* add the menu for this processor, because the subplugin
2055 menu is always cleared at the top of processors_changed().
2056 this is the result of some poor design in gtkmm and/or
2060 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2065 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2066 RouteTimeAxisView::ProcessorAutomationNode* pan)
2068 bool showit = pan->menu_item->get_active();
2069 bool redraw = false;
2071 if (pan->view == 0 && showit) {
2072 add_processor_automation_curve (rai->processor, pan->what);
2076 if (pan->view && pan->view->set_marked_for_display (showit)) {
2080 if (redraw && !no_redraw) {
2086 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2088 if (c.type == RouteProcessorChange::MeterPointChange) {
2089 /* nothing to do if only the meter point has changed */
2093 using namespace Menu_Helpers;
2095 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2096 (*i)->valid = false;
2099 setup_processor_menu_and_curves ();
2101 bool deleted_processor_automation = false;
2103 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2105 list<ProcessorAutomationInfo*>::iterator tmp;
2113 processor_automation.erase (i);
2114 deleted_processor_automation = true;
2121 if (deleted_processor_automation && !no_redraw) {
2126 boost::shared_ptr<AutomationLine>
2127 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2129 ProcessorAutomationNode* pan;
2131 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2137 return boost::shared_ptr<AutomationLine>();
2141 RouteTimeAxisView::reset_processor_automation_curves ()
2143 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2149 RouteTimeAxisView::can_edit_name () const
2151 /* we do not allow track name changes if it is record enabled
2153 return !_route->record_enabled();
2157 RouteTimeAxisView::update_rec_display ()
2159 RouteUI::update_rec_display ();
2163 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2165 if (_ignore_set_layer_display) {
2169 if (apply_to_selection) {
2170 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2174 _view->set_layer_display (d);
2177 set_gui_property (X_("layer-display"), enum_2_string (d));
2182 RouteTimeAxisView::layer_display () const
2185 return _view->layer_display ();
2188 /* we don't know, since we don't have a _view, so just return something */
2194 boost::shared_ptr<AutomationTimeAxisView>
2195 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2197 AutomationTracks::iterator i = _automation_tracks.find(param);
2198 if (i != _automation_tracks.end()) {
2201 return boost::shared_ptr<AutomationTimeAxisView>();
2206 RouteTimeAxisView::fast_update ()
2208 gm.get_level_meter().update_meters ();
2212 RouteTimeAxisView::hide_meter ()
2215 gm.get_level_meter().hide_meters ();
2219 RouteTimeAxisView::show_meter ()
2225 RouteTimeAxisView::reset_meter ()
2227 if (Config->get_show_track_meters()) {
2228 int meter_width = 3;
2229 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2232 gm.get_level_meter().setup_meters (height - 9, meter_width);
2239 RouteTimeAxisView::clear_meter ()
2241 gm.get_level_meter().clear_meters ();
2245 RouteTimeAxisView::meter_changed ()
2247 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2249 if (_route && !no_redraw) {
2255 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2258 if (_route && !no_redraw) {
2264 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2266 using namespace Menu_Helpers;
2268 if (!_underlay_streams.empty()) {
2269 MenuList& parent_items = parent_menu->items();
2270 Menu* gs_menu = manage (new Menu);
2271 gs_menu->set_name ("ArdourContextMenu");
2272 MenuList& gs_items = gs_menu->items();
2274 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2276 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2277 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2278 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2284 RouteTimeAxisView::set_underlay_state()
2286 if (!underlay_xml_node) {
2290 XMLNodeList nlist = underlay_xml_node->children();
2291 XMLNodeConstIterator niter;
2292 XMLNode *child_node;
2294 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2295 child_node = *niter;
2297 if (child_node->name() != "Underlay") {
2301 XMLProperty* prop = child_node->property ("id");
2303 PBD::ID id (prop->value());
2305 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2308 add_underlay(v->view(), false);
2317 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2323 RouteTimeAxisView& other = v->trackview();
2325 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2326 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2327 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2331 _underlay_streams.push_back(v);
2332 other._underlay_mirrors.push_back(this);
2334 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2336 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2338 if (!underlay_xml_node) {
2339 underlay_xml_node = xml_node->add_child("Underlays");
2342 XMLNode* node = underlay_xml_node->add_child("Underlay");
2343 XMLProperty* prop = node->add_property("id");
2344 prop->set_value(v->trackview().route()->id().to_s());
2351 RouteTimeAxisView::remove_underlay (StreamView* v)
2357 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2358 RouteTimeAxisView& other = v->trackview();
2360 if (it != _underlay_streams.end()) {
2361 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2363 if (gm == other._underlay_mirrors.end()) {
2364 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2368 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2370 _underlay_streams.erase(it);
2371 other._underlay_mirrors.erase(gm);
2373 if (underlay_xml_node) {
2374 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2380 RouteTimeAxisView::set_button_names ()
2382 if (_route && _route->solo_safe()) {
2383 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2385 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2387 if (Config->get_solo_control_is_listen_control()) {
2388 switch (Config->get_listen_position()) {
2389 case AfterFaderListen:
2390 solo_button->set_text (_("A"));
2391 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2393 case PreFaderListen:
2394 solo_button->set_text (_("P"));
2395 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2399 solo_button->set_text (_("s"));
2400 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2402 mute_button->set_text (_("m"));
2406 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2408 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2409 if (i != _main_automation_menu_map.end()) {
2413 i = _subplugin_menu_map.find (param);
2414 if (i != _subplugin_menu_map.end()) {
2422 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2424 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2426 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2430 gain_track.reset (new AutomationTimeAxisView (_session,
2431 _route, _route->amp(), c, param,
2436 _route->amp()->describe_parameter(param)));
2439 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2442 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2446 void add_region_to_list (RegionView* rv, RegionList* l)
2448 l->push_back (rv->region());
2452 RouteTimeAxisView::combine_regions ()
2454 /* as of may 2011, we do not offer uncombine for MIDI tracks
2457 if (!is_audio_track()) {
2465 RegionList selected_regions;
2466 boost::shared_ptr<Playlist> playlist = track()->playlist();
2468 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2470 if (selected_regions.size() < 2) {
2474 playlist->clear_changes ();
2475 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2477 _session->add_command (new StatefulDiffCommand (playlist));
2478 /* make the new region be selected */
2480 return _view->find_view (compound_region);
2484 RouteTimeAxisView::uncombine_regions ()
2486 /* as of may 2011, we do not offer uncombine for MIDI tracks
2488 if (!is_audio_track()) {
2496 RegionList selected_regions;
2497 boost::shared_ptr<Playlist> playlist = track()->playlist();
2499 /* have to grab selected regions first because the uncombine is going
2500 * to change that in the middle of the list traverse
2503 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2505 playlist->clear_changes ();
2507 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2508 playlist->uncombine (*i);
2511 _session->add_command (new StatefulDiffCommand (playlist));
2515 RouteTimeAxisView::state_id() const
2517 return string_compose ("rtav %1", _route->id().to_s());
2522 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2524 TimeAxisView::remove_child (c);
2526 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2528 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2529 if (i->second == a) {
2530 _automation_tracks.erase (i);