2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
91 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
93 , parent_canvas (canvas)
96 , route_group_button (_("g"))
97 , playlist_button (_("p"))
98 , automation_button (_("a"))
99 , automation_action_menu (0)
100 , plugins_submenu_item (0)
101 , route_group_menu (0)
102 , playlist_action_menu (0)
104 , color_mode_menu (0)
105 , gm (sess, true, 125, 18)
106 , _ignore_set_layer_display (false)
111 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
113 RouteUI::set_route (rt);
115 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
116 gm.get_level_meter().set_no_show_all();
117 gm.get_level_meter().setup_meters(50);
118 gm.update_gain_sensitive ();
120 string str = gui_property ("height");
122 set_height (atoi (str));
124 set_height (preset_height (HeightNormal));
127 if (!_route->is_hidden()) {
128 if (gui_property ("visible").empty()) {
129 set_gui_property ("visible", true);
132 set_gui_property ("visible", false);
136 update_solo_display ();
138 timestretch_rect = 0;
141 ignore_toggle = false;
143 route_group_button.set_name ("route button");
144 playlist_button.set_name ("route button");
145 automation_button.set_name ("route button");
147 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
148 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
149 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
155 switch (track()->mode()) {
157 case ARDOUR::NonLayered:
158 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
160 case ARDOUR::Destructive:
161 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
165 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
167 if (is_midi_track()) {
168 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
169 gm.set_fader_name ("MidiTrackFader");
171 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
172 gm.set_fader_name ("AudioTrackFader");
175 rec_enable_button->set_sensitive (_session->writable());
177 /* set playlist button tip to the current playlist, and make it update when it changes */
178 update_playlist_tip ();
179 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
182 gm.set_fader_name ("AudioBusFader");
185 controls_hbox.pack_start(gm.get_level_meter(), false, false);
186 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
187 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
188 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
190 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 if (!_route->is_master()) {
193 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
196 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
197 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
199 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
200 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
201 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
203 if (is_midi_track()) {
204 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
206 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
211 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
213 if (is_track() && track()->mode() == ARDOUR::Normal) {
214 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
220 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
224 str = gui_property ("layer-display");
226 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
229 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
230 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
232 /* pick up the correct freeze state */
237 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
238 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
239 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
241 PropertyList* plist = new PropertyList();
243 plist->add (ARDOUR::Properties::mute, true);
244 plist->add (ARDOUR::Properties::solo, true);
246 route_group_menu = new RouteGroupMenu (_session, plist);
248 // gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
250 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
253 RouteTimeAxisView::~RouteTimeAxisView ()
255 CatchDeletion (this);
257 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
261 delete playlist_action_menu;
262 playlist_action_menu = 0;
267 _automation_tracks.clear ();
269 delete route_group_menu;
273 RouteTimeAxisView::post_construct ()
275 /* map current state of the route */
277 update_diskstream_display ();
278 setup_processor_menu_and_curves ();
279 reset_processor_automation_curves ();
282 /** Set up the processor menu for the current set of processors, and
283 * display automation curves for any parameters which have data.
286 RouteTimeAxisView::setup_processor_menu_and_curves ()
288 _subplugin_menu_map.clear ();
289 subplugin_menu.items().clear ();
290 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
291 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
295 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
297 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
298 if (_route->route_group()) {
299 _route->route_group()->remove (_route);
305 r.push_back (route ());
307 route_group_menu->build (r);
308 route_group_menu->menu()->popup (ev->button, ev->time);
314 RouteTimeAxisView::playlist_changed ()
320 RouteTimeAxisView::label_view ()
322 string x = _route->name();
324 if (x != name_label.get_text()) {
325 name_label.set_text (x);
331 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
333 if (what_changed.contains (ARDOUR::Properties::name)) {
339 RouteTimeAxisView::take_name_changed (void *src)
347 RouteTimeAxisView::playlist_click ()
349 build_playlist_menu ();
350 conditionally_add_to_selection ();
351 playlist_action_menu->popup (1, gtk_get_current_event_time());
355 RouteTimeAxisView::automation_click ()
357 conditionally_add_to_selection ();
358 build_automation_action_menu (false);
359 automation_action_menu->popup (1, gtk_get_current_event_time());
363 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
365 using namespace Menu_Helpers;
367 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
368 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
371 detach_menu (subplugin_menu);
373 _main_automation_menu_map.clear ();
374 delete automation_action_menu;
375 automation_action_menu = new Menu;
377 MenuList& items = automation_action_menu->items();
379 automation_action_menu->set_name ("ArdourContextMenu");
381 items.push_back (MenuElem (_("Show All Automation"),
382 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
384 items.push_back (MenuElem (_("Show Existing Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
387 items.push_back (MenuElem (_("Hide All Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
390 items.push_back (SeparatorElem ());
392 /* Attach the plugin submenu. It may have previously been used elsewhere,
393 so it was detached above
396 if (!subplugin_menu.items().empty()) {
397 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
398 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
403 RouteTimeAxisView::build_display_menu ()
405 using namespace Menu_Helpers;
409 TimeAxisView::build_display_menu ();
411 /* now fill it with our stuff */
413 MenuList& items = display_menu->items();
414 display_menu->set_name ("ArdourContextMenu");
416 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
419 detach_menu (*_size_menu);
422 items.push_back (MenuElem (_("Height"), *_size_menu));
424 items.push_back (SeparatorElem());
426 if (!Profile->get_sae()) {
427 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
428 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
429 items.push_back (SeparatorElem());
432 // Hook for derived classes to add type specific stuff
433 append_extra_display_menu_items ();
437 Menu* layers_menu = manage (new Menu);
438 MenuList &layers_items = layers_menu->items();
439 layers_menu->set_name("ArdourContextMenu");
441 RadioMenuItem::Group layers_group;
443 /* Find out how many overlaid/stacked tracks we have in the selection */
447 TrackSelection const & s = _editor.get_selection().tracks;
448 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
449 StreamView* v = (*i)->view ();
454 switch (v->layer_display ()) {
465 /* We're not connecting to signal_toggled() here; in the case where these two items are
466 set to be in the `inconsistent' state, it seems that one or other will end up active
467 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
468 select the active one, no toggled signal is emitted so nothing happens.
471 _ignore_set_layer_display = true;
473 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
474 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
475 i->set_active (overlaid != 0 && stacked == 0);
476 i->set_inconsistent (overlaid != 0 && stacked != 0);
477 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
479 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
480 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
481 i->set_active (overlaid == 0 && stacked != 0);
482 i->set_inconsistent (overlaid != 0 && stacked != 0);
483 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
485 _ignore_set_layer_display = false;
487 items.push_back (MenuElem (_("Layers"), *layers_menu));
489 if (!Profile->get_sae()) {
491 Menu* alignment_menu = manage (new Menu);
492 MenuList& alignment_items = alignment_menu->items();
493 alignment_menu->set_name ("ArdourContextMenu");
495 RadioMenuItem::Group align_group;
497 /* Same verbose hacks as for the layering options above */
503 boost::shared_ptr<Track> first_track;
505 TrackSelection const & s = _editor.get_selection().tracks;
506 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
507 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
508 if (!r || !r->is_track ()) {
513 first_track = r->track();
516 switch (r->track()->alignment_choice()) {
520 switch (r->track()->alignment_style()) {
521 case ExistingMaterial:
529 case UseExistingMaterial:
545 inconsistent = false;
554 if (!inconsistent && first_track) {
556 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
557 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
558 i->set_active (automatic != 0 && existing == 0 && capture == 0);
559 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
561 switch (first_track->alignment_choice()) {
563 switch (first_track->alignment_style()) {
564 case ExistingMaterial:
565 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
568 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
576 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
577 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
578 i->set_active (existing != 0 && capture == 0 && automatic == 0);
579 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
581 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
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, UseCaptureTime, true));
586 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
592 Menu* mode_menu = manage (new Menu);
593 MenuList& mode_items = mode_menu->items ();
594 mode_menu->set_name ("ArdourContextMenu");
596 RadioMenuItem::Group mode_group;
602 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
603 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
604 if (!r || !r->is_track ()) {
608 switch (r->track()->mode()) {
621 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
622 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
623 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
624 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
625 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
627 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
628 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
629 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
630 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
631 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
633 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
636 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
637 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
639 items.push_back (MenuElem (_("Mode"), *mode_menu));
643 items.push_back (SeparatorElem());
645 build_playlist_menu ();
646 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
647 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
650 route_group_menu->detach ();
653 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
654 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
656 r.push_back (rtv->route ());
661 r.push_back (route ());
664 route_group_menu->build (r);
665 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
667 build_automation_action_menu (true);
668 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
670 items.push_back (SeparatorElem());
674 TrackSelection const & s = _editor.get_selection().tracks;
675 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
676 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
681 if (r->route()->active()) {
688 items.push_back (CheckMenuElem (_("Active")));
689 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
690 bool click_sets_active = true;
691 if (active > 0 && inactive == 0) {
692 i->set_active (true);
693 click_sets_active = false;
694 } else if (active > 0 && inactive > 0) {
695 i->set_inconsistent (true);
697 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
699 items.push_back (SeparatorElem());
700 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
701 if (!Profile->get_sae()) {
702 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
704 items.push_front (SeparatorElem());
705 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
710 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
712 if (apply_to_selection) {
713 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
718 if (!track()->can_use_mode (mode, needs_bounce)) {
724 cerr << "would bounce this one\n";
729 track()->set_mode (mode);
731 rec_enable_button->remove ();
734 case ARDOUR::NonLayered:
736 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
737 rec_enable_button->set_text (string());
739 case ARDOUR::Destructive:
740 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
741 rec_enable_button->set_text (string());
745 rec_enable_button->show_all ();
750 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
752 TimeAxisView::show_timestretch (start, end, layers, layer);
762 /* check that the time selection was made in our route, or our route group.
763 remember that route_group() == 0 implies the route is *not* in a edit group.
766 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
767 /* this doesn't apply to us */
771 /* ignore it if our edit group is not active */
773 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
778 if (timestretch_rect == 0) {
779 timestretch_rect = new SimpleRect (*canvas_display ());
780 timestretch_rect->property_x1() = 0.0;
781 timestretch_rect->property_y1() = 0.0;
782 timestretch_rect->property_x2() = 0.0;
783 timestretch_rect->property_y2() = 0.0;
784 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
785 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
788 timestretch_rect->show ();
789 timestretch_rect->raise_to_top ();
791 double const x1 = start / _editor.get_current_zoom();
792 double const x2 = (end - 1) / _editor.get_current_zoom();
794 timestretch_rect->property_x1() = x1;
795 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
796 timestretch_rect->property_x2() = x2;
797 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
801 RouteTimeAxisView::hide_timestretch ()
803 TimeAxisView::hide_timestretch ();
805 if (timestretch_rect) {
806 timestretch_rect->hide ();
811 RouteTimeAxisView::show_selection (TimeSelection& ts)
815 /* ignore it if our edit group is not active or if the selection was started
816 in some other track or route group (remember that route_group() == 0 means
817 that the track is not in an route group).
820 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
821 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
827 TimeAxisView::show_selection (ts);
831 RouteTimeAxisView::set_height (uint32_t h)
834 bool height_changed = (height == 0) || (h != height);
835 gm.get_level_meter().setup_meters (gmlen);
837 TimeAxisView::set_height (h);
840 _view->set_height ((double) current_height());
843 if (height >= preset_height (HeightNormal)) {
847 gm.get_gain_slider().show();
849 if (!_route || _route->is_monitor()) {
854 if (rec_enable_button)
855 rec_enable_button->show();
857 route_group_button.show();
858 automation_button.show();
860 if (is_track() && track()->mode() == ARDOUR::Normal) {
861 playlist_button.show();
868 gm.get_gain_slider().hide();
870 if (!_route || _route->is_monitor()) {
875 if (rec_enable_button)
876 rec_enable_button->show();
878 route_group_button.hide ();
879 automation_button.hide ();
881 if (is_track() && track()->mode() == ARDOUR::Normal) {
882 playlist_button.hide ();
887 if (height_changed && !no_redraw) {
888 /* only emit the signal if the height really changed */
894 RouteTimeAxisView::route_color_changed ()
897 _view->apply_color (color(), StreamView::RegionColor);
902 RouteTimeAxisView::reset_samples_per_unit ()
904 set_samples_per_unit (_editor.get_current_zoom());
908 RouteTimeAxisView::horizontal_position_changed ()
911 _view->horizontal_position_changed ();
916 RouteTimeAxisView::set_samples_per_unit (double spu)
921 speed = track()->speed();
925 _view->set_samples_per_unit (spu * speed);
928 TimeAxisView::set_samples_per_unit (spu * speed);
932 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
934 if (!mitem->get_active()) {
935 /* this is one of the two calls made when these radio menu items change status. this one
936 is for the item that became inactive, and we want to ignore it.
941 if (apply_to_selection) {
942 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
945 track()->set_align_choice (choice);
951 RouteTimeAxisView::rename_current_playlist ()
953 ArdourPrompter prompter (true);
956 boost::shared_ptr<Track> tr = track();
957 if (!tr || tr->destructive()) {
961 boost::shared_ptr<Playlist> pl = tr->playlist();
966 prompter.set_title (_("Rename Playlist"));
967 prompter.set_prompt (_("New name for playlist:"));
968 prompter.set_initial_text (pl->name());
969 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
970 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
972 switch (prompter.run ()) {
973 case Gtk::RESPONSE_ACCEPT:
974 prompter.get_result (name);
986 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
988 std::string ret (basename);
990 std::string const group_string = "." + route_group()->name() + ".";
992 // iterate through all playlists
994 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
995 std::string tmp = (*i)->name();
997 std::string::size_type idx = tmp.find(group_string);
998 // find those which belong to this group
999 if (idx != string::npos) {
1000 tmp = tmp.substr(idx + group_string.length());
1002 // and find the largest current number
1003 int x = atoi(tmp.c_str());
1004 if (x > maxnumber) {
1013 snprintf (buf, sizeof(buf), "%d", maxnumber);
1015 ret = this->name() + "." + route_group()->name () + "." + buf;
1021 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1025 boost::shared_ptr<Track> tr = track ();
1026 if (!tr || tr->destructive()) {
1030 boost::shared_ptr<const Playlist> pl = tr->playlist();
1037 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1038 name = resolve_new_group_playlist_name(name, playlists_before_op);
1041 while (_session->playlists->by_name(name)) {
1042 name = Playlist::bump_name (name, *_session);
1045 // TODO: The prompter "new" button should be de-activated if the user
1046 // specifies a playlist name which already exists in the session.
1050 ArdourPrompter prompter (true);
1052 prompter.set_title (_("New Copy Playlist"));
1053 prompter.set_prompt (_("Name for new playlist:"));
1054 prompter.set_initial_text (name);
1055 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1056 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1057 prompter.show_all ();
1059 switch (prompter.run ()) {
1060 case Gtk::RESPONSE_ACCEPT:
1061 prompter.get_result (name);
1069 if (name.length()) {
1070 tr->use_copy_playlist ();
1071 tr->playlist()->set_name (name);
1076 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1080 boost::shared_ptr<Track> tr = track ();
1081 if (!tr || tr->destructive()) {
1085 boost::shared_ptr<const Playlist> pl = tr->playlist();
1092 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1093 name = resolve_new_group_playlist_name(name,playlists_before_op);
1096 while (_session->playlists->by_name(name)) {
1097 name = Playlist::bump_name (name, *_session);
1103 ArdourPrompter prompter (true);
1105 prompter.set_title (_("New Playlist"));
1106 prompter.set_prompt (_("Name for new playlist:"));
1107 prompter.set_initial_text (name);
1108 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1109 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1111 switch (prompter.run ()) {
1112 case Gtk::RESPONSE_ACCEPT:
1113 prompter.get_result (name);
1121 if (name.length()) {
1122 tr->use_new_playlist ();
1123 tr->playlist()->set_name (name);
1128 RouteTimeAxisView::clear_playlist ()
1130 boost::shared_ptr<Track> tr = track ();
1131 if (!tr || tr->destructive()) {
1135 boost::shared_ptr<Playlist> pl = tr->playlist();
1140 _editor.clear_playlist (pl);
1144 RouteTimeAxisView::speed_changed ()
1146 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1150 RouteTimeAxisView::update_diskstream_display ()
1160 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1162 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1164 /* special case: select/deselect all tracks */
1165 if (_editor.get_selection().selected (this)) {
1166 _editor.get_selection().clear_tracks ();
1168 _editor.select_all_tracks ();
1174 switch (ArdourKeyboard::selection_type (ev->state)) {
1175 case Selection::Toggle:
1176 _editor.get_selection().toggle (this);
1179 case Selection::Set:
1180 _editor.get_selection().set (this);
1183 case Selection::Extend:
1184 _editor.extend_selection_to_track (*this);
1187 case Selection::Add:
1188 _editor.get_selection().add (this);
1194 RouteTimeAxisView::set_selected_points (PointSelection& points)
1196 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1197 (*i)->set_selected_points (points);
1202 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1205 _view->set_selected_regionviews (regions);
1209 /** Add the selectable things that we have to a list.
1210 * @param results List to add things to.
1213 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1218 speed = track()->speed();
1221 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1222 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1224 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1225 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1228 /* pick up visible automation tracks */
1230 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1231 if (!(*i)->hidden()) {
1232 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1238 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1241 _view->get_inverted_selectables (sel, results);
1244 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1245 if (!(*i)->hidden()) {
1246 (*i)->get_inverted_selectables (sel, results);
1254 RouteTimeAxisView::route_group () const
1256 return _route->route_group();
1260 RouteTimeAxisView::name() const
1262 return _route->name();
1265 boost::shared_ptr<Playlist>
1266 RouteTimeAxisView::playlist () const
1268 boost::shared_ptr<Track> tr;
1270 if ((tr = track()) != 0) {
1271 return tr->playlist();
1273 return boost::shared_ptr<Playlist> ();
1278 RouteTimeAxisView::name_entry_changed ()
1280 TimeAxisView::name_entry_changed ();
1282 string x = name_entry->get_text ();
1284 if (x == _route->name()) {
1288 strip_whitespace_edges (x);
1290 if (x.length() == 0) {
1291 name_entry->set_text (_route->name());
1295 if (_session->route_name_internal (x)) {
1296 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1298 name_entry->grab_focus ();
1299 } else if (RouteUI::verify_new_route_name (x)) {
1300 _route->set_name (x);
1302 name_entry->grab_focus ();
1306 boost::shared_ptr<Region>
1307 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1309 boost::shared_ptr<Playlist> pl = playlist ();
1312 return pl->find_next_region (pos, point, dir);
1315 return boost::shared_ptr<Region> ();
1319 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1321 boost::shared_ptr<Playlist> pl = playlist ();
1324 return pl->find_next_region_boundary (pos, dir);
1331 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1333 boost::shared_ptr<Playlist> what_we_got;
1334 boost::shared_ptr<Track> tr = track ();
1335 boost::shared_ptr<Playlist> playlist;
1338 /* route is a bus, not a track */
1342 playlist = tr->playlist();
1344 TimeSelection time (selection.time);
1345 float const speed = tr->speed();
1346 if (speed != 1.0f) {
1347 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1348 (*i).start = session_frame_to_track_frame((*i).start, speed);
1349 (*i).end = session_frame_to_track_frame((*i).end, speed);
1353 playlist->clear_changes ();
1354 playlist->clear_owned_changes ();
1358 if (playlist->cut (time) != 0) {
1359 vector<Command*> cmds;
1360 playlist->rdiff (cmds);
1361 _session->add_commands (cmds);
1363 _session->add_command (new StatefulDiffCommand (playlist));
1368 if ((what_we_got = playlist->cut (time)) != 0) {
1369 _editor.get_cut_buffer().add (what_we_got);
1370 vector<Command*> cmds;
1371 playlist->rdiff (cmds);
1372 _session->add_commands (cmds);
1374 _session->add_command (new StatefulDiffCommand (playlist));
1378 if ((what_we_got = playlist->copy (time)) != 0) {
1379 _editor.get_cut_buffer().add (what_we_got);
1384 if ((what_we_got = playlist->cut (time)) != 0) {
1386 vector<Command*> cmds;
1387 playlist->rdiff (cmds);
1388 _session->add_commands (cmds);
1389 _session->add_command (new StatefulDiffCommand (playlist));
1390 what_we_got->release ();
1397 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1403 boost::shared_ptr<Playlist> pl = playlist ();
1404 PlaylistSelection::iterator p;
1406 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1408 if (p == selection.playlists.end()) {
1412 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1414 if (track()->speed() != 1.0f) {
1415 pos = session_frame_to_track_frame (pos, track()->speed());
1416 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1419 pl->clear_changes ();
1420 pl->paste (*p, pos, times);
1421 _session->add_command (new StatefulDiffCommand (pl));
1427 struct PlaylistSorter {
1428 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1429 return a->sort_id() < b->sort_id();
1434 RouteTimeAxisView::build_playlist_menu ()
1436 using namespace Menu_Helpers;
1442 delete playlist_action_menu;
1443 playlist_action_menu = new Menu;
1444 playlist_action_menu->set_name ("ArdourContextMenu");
1446 MenuList& playlist_items = playlist_action_menu->items();
1447 playlist_action_menu->set_name ("ArdourContextMenu");
1448 playlist_items.clear();
1450 RadioMenuItem::Group playlist_group;
1451 boost::shared_ptr<Track> tr = track ();
1453 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1455 /* sort the playlists */
1457 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1459 /* add the playlists to the menu */
1460 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1461 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1462 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1463 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1465 if (tr->playlist()->id() == (*i)->id()) {
1471 playlist_items.push_back (SeparatorElem());
1472 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1473 playlist_items.push_back (SeparatorElem());
1475 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1476 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1477 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1480 // Use a label which tells the user what is happening
1481 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1482 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1486 playlist_items.push_back (SeparatorElem());
1487 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1488 playlist_items.push_back (SeparatorElem());
1490 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1494 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1496 assert (is_track());
1498 // exit if we were triggered by deactivating the old playlist
1499 if (!item->get_active()) {
1503 boost::shared_ptr<Playlist> pl (wpl.lock());
1509 if (track()->playlist() == pl) {
1510 // exit when use_playlist is called by the creation of the playlist menu
1511 // or the playlist choice is unchanged
1515 track()->use_playlist (pl);
1517 RouteGroup* rg = route_group();
1519 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1520 std::string group_string = "." + rg->name() + ".";
1522 std::string take_name = pl->name();
1523 std::string::size_type idx = take_name.find(group_string);
1525 if (idx == std::string::npos)
1528 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1530 boost::shared_ptr<RouteList> rl (rg->route_list());
1532 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1533 if ((*i) == this->route()) {
1537 std::string playlist_name = (*i)->name()+group_string+take_name;
1539 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1544 if (track->freeze_state() == Track::Frozen) {
1545 /* Don't change playlists of frozen tracks */
1549 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1551 // No playlist for this track for this take yet, make it
1552 track->use_new_playlist();
1553 track->playlist()->set_name(playlist_name);
1555 track->use_playlist(ipl);
1562 RouteTimeAxisView::update_playlist_tip ()
1564 RouteGroup* rg = route_group ();
1565 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1566 string group_string = "." + rg->name() + ".";
1568 string take_name = track()->playlist()->name();
1569 string::size_type idx = take_name.find(group_string);
1571 if (idx != string::npos) {
1572 /* find the bit containing the take number / name */
1573 take_name = take_name.substr (idx + group_string.length());
1575 /* set the playlist button tooltip to the take name */
1576 ARDOUR_UI::instance()->set_tip (
1578 string_compose(_("Take: %1.%2"),
1579 Glib::Markup::escape_text(rg->name()),
1580 Glib::Markup::escape_text(take_name))
1587 /* set the playlist button tooltip to the playlist name */
1588 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1593 RouteTimeAxisView::show_playlist_selector ()
1595 _editor.playlist_selector().show_for (this);
1599 RouteTimeAxisView::map_frozen ()
1605 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1607 switch (track()->freeze_state()) {
1609 playlist_button.set_sensitive (false);
1610 rec_enable_button->set_sensitive (false);
1613 playlist_button.set_sensitive (true);
1614 rec_enable_button->set_sensitive (true);
1620 RouteTimeAxisView::color_handler ()
1622 //case cTimeStretchOutline:
1623 if (timestretch_rect) {
1624 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1626 //case cTimeStretchFill:
1627 if (timestretch_rect) {
1628 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1634 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1635 * Will add track if necessary.
1638 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1640 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1641 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1644 /* it doesn't exist yet, so we don't care about the button state: just add it */
1645 create_automation_child (param, true);
1648 bool yn = menu->get_active();
1649 bool changed = false;
1651 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1653 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1654 will have done that for us.
1657 if (changed && !no_redraw) {
1665 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1667 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1673 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1675 if (menu && !_hidden) {
1676 ignore_toggle = true;
1677 menu->set_active (false);
1678 ignore_toggle = false;
1681 if (_route && !no_redraw) {
1688 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1690 if (apply_to_selection) {
1691 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1695 /* Show our automation */
1697 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1698 i->second->set_marked_for_display (true);
1700 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1703 menu->set_active(true);
1708 /* Show processor automation */
1710 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1711 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1712 if ((*ii)->view == 0) {
1713 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1716 (*ii)->menu_item->set_active (true);
1729 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1731 if (apply_to_selection) {
1732 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1736 /* Show our automation */
1738 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1739 if (i->second->has_automation()) {
1740 i->second->set_marked_for_display (true);
1742 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1744 menu->set_active(true);
1749 /* Show processor automation */
1751 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1752 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1753 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1754 (*ii)->menu_item->set_active (true);
1766 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1768 if (apply_to_selection) {
1769 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1773 /* Hide our automation */
1775 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1776 i->second->set_marked_for_display (false);
1778 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1781 menu->set_active (false);
1785 /* Hide processor automation */
1787 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1788 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1789 (*ii)->menu_item->set_active (false);
1800 RouteTimeAxisView::region_view_added (RegionView* rv)
1802 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1803 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1804 boost::shared_ptr<AutomationTimeAxisView> atv;
1806 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1811 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1812 (*i)->add_ghost(rv);
1816 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1818 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1824 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1826 parent.remove_processor_automation_node (this);
1830 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1833 remove_child (pan->view);
1837 RouteTimeAxisView::ProcessorAutomationNode*
1838 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1840 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1842 if ((*i)->processor == processor) {
1844 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1845 if ((*ii)->what == what) {
1855 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1857 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1860 ProcessorAutomationNode* pan;
1862 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1863 /* session state may never have been saved with new plugin */
1864 error << _("programming error: ")
1865 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1866 processor->name(), what.type(), (int) what.channel(), what.id() )
1876 boost::shared_ptr<AutomationControl> control
1877 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1879 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1880 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1881 _editor, *this, false, parent_canvas,
1882 processor->describe_parameter (what), processor->name()));
1884 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1886 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1889 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1894 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1897 pan->menu_item->set_active (false);
1906 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1908 boost::shared_ptr<Processor> processor (p.lock ());
1910 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1911 /* The Amp processor is a special case and is dealt with separately */
1915 set<Evoral::Parameter> existing;
1917 processor->what_has_data (existing);
1919 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1921 Evoral::Parameter param (*i);
1922 boost::shared_ptr<AutomationLine> al;
1924 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1927 add_processor_automation_curve (processor, param);
1933 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1935 using namespace Menu_Helpers;
1939 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1941 _automation_tracks[param] = track;
1943 /* existing state overrides "show" argument */
1944 string s = track->gui_property ("visible");
1946 show = string_is_affirmative (s);
1949 /* this might or might not change the visibility status, so don't rely on it */
1950 track->set_marked_for_display (show);
1952 if (show && !no_redraw) {
1956 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1957 /* MIDI-related parameters are always in the menu, there's no
1958 reason to rebuild the menu just because we added a automation
1959 lane for one of them. But if we add a non-MIDI automation
1960 lane, then we need to invalidate the display menu.
1962 delete display_menu;
1968 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1970 boost::shared_ptr<Processor> processor (p.lock ());
1972 if (!processor || !processor->display_to_user ()) {
1976 /* we use this override to veto the Amp processor from the plugin menu,
1977 as its automation lane can be accessed using the special "Fader" menu
1981 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1985 using namespace Menu_Helpers;
1986 ProcessorAutomationInfo *rai;
1987 list<ProcessorAutomationInfo*>::iterator x;
1989 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1991 if (automatable.empty()) {
1995 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1996 if ((*x)->processor == processor) {
2001 if (x == processor_automation.end()) {
2003 rai = new ProcessorAutomationInfo (processor);
2004 processor_automation.push_back (rai);
2012 /* any older menu was deleted at the top of processors_changed()
2013 when we cleared the subplugin menu.
2016 rai->menu = manage (new Menu);
2017 MenuList& items = rai->menu->items();
2018 rai->menu->set_name ("ArdourContextMenu");
2022 std::set<Evoral::Parameter> has_visible_automation;
2023 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2025 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2027 ProcessorAutomationNode* pan;
2028 CheckMenuItem* mitem;
2030 string name = processor->describe_parameter (*i);
2032 items.push_back (CheckMenuElem (name));
2033 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2035 _subplugin_menu_map[*i] = mitem;
2037 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2038 mitem->set_active(true);
2041 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2045 pan = new ProcessorAutomationNode (*i, mitem, *this);
2047 rai->lines.push_back (pan);
2051 pan->menu_item = mitem;
2055 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2058 /* add the menu for this processor, because the subplugin
2059 menu is always cleared at the top of processors_changed().
2060 this is the result of some poor design in gtkmm and/or
2064 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2069 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2070 RouteTimeAxisView::ProcessorAutomationNode* pan)
2072 bool showit = pan->menu_item->get_active();
2073 bool redraw = false;
2075 if (pan->view == 0 && showit) {
2076 add_processor_automation_curve (rai->processor, pan->what);
2080 if (pan->view && pan->view->set_marked_for_display (showit)) {
2084 if (redraw && !no_redraw) {
2090 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2092 if (c.type == RouteProcessorChange::MeterPointChange) {
2093 /* nothing to do if only the meter point has changed */
2097 using namespace Menu_Helpers;
2099 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2100 (*i)->valid = false;
2103 setup_processor_menu_and_curves ();
2105 bool deleted_processor_automation = false;
2107 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2109 list<ProcessorAutomationInfo*>::iterator tmp;
2117 processor_automation.erase (i);
2118 deleted_processor_automation = true;
2125 if (deleted_processor_automation && !no_redraw) {
2130 boost::shared_ptr<AutomationLine>
2131 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2133 ProcessorAutomationNode* pan;
2135 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2141 return boost::shared_ptr<AutomationLine>();
2145 RouteTimeAxisView::reset_processor_automation_curves ()
2147 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2153 RouteTimeAxisView::can_edit_name () const
2155 /* we do not allow track name changes if it is record enabled
2157 return !_route->record_enabled();
2161 RouteTimeAxisView::update_rec_display ()
2163 RouteUI::update_rec_display ();
2167 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2169 if (_ignore_set_layer_display) {
2173 if (apply_to_selection) {
2174 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2178 _view->set_layer_display (d);
2181 set_gui_property (X_("layer-display"), enum_2_string (d));
2186 RouteTimeAxisView::layer_display () const
2189 return _view->layer_display ();
2192 /* we don't know, since we don't have a _view, so just return something */
2198 boost::shared_ptr<AutomationTimeAxisView>
2199 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2201 AutomationTracks::iterator i = _automation_tracks.find(param);
2202 if (i != _automation_tracks.end()) {
2205 return boost::shared_ptr<AutomationTimeAxisView>();
2210 RouteTimeAxisView::fast_update ()
2212 gm.get_level_meter().update_meters ();
2216 RouteTimeAxisView::hide_meter ()
2219 gm.get_level_meter().hide_meters ();
2223 RouteTimeAxisView::show_meter ()
2229 RouteTimeAxisView::reset_meter ()
2231 if (Config->get_show_track_meters()) {
2232 gm.get_level_meter().setup_meters (height-5);
2239 RouteTimeAxisView::clear_meter ()
2241 gm.get_level_meter().clear_meters ();
2245 RouteTimeAxisView::meter_changed ()
2247 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2252 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2258 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2260 using namespace Menu_Helpers;
2262 if (!_underlay_streams.empty()) {
2263 MenuList& parent_items = parent_menu->items();
2264 Menu* gs_menu = manage (new Menu);
2265 gs_menu->set_name ("ArdourContextMenu");
2266 MenuList& gs_items = gs_menu->items();
2268 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2270 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2271 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2272 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2278 RouteTimeAxisView::set_underlay_state()
2280 if (!underlay_xml_node) {
2284 XMLNodeList nlist = underlay_xml_node->children();
2285 XMLNodeConstIterator niter;
2286 XMLNode *child_node;
2288 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2289 child_node = *niter;
2291 if (child_node->name() != "Underlay") {
2295 XMLProperty* prop = child_node->property ("id");
2297 PBD::ID id (prop->value());
2299 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2302 add_underlay(v->view(), false);
2311 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2317 RouteTimeAxisView& other = v->trackview();
2319 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2320 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2321 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2325 _underlay_streams.push_back(v);
2326 other._underlay_mirrors.push_back(this);
2328 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2330 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2332 if (!underlay_xml_node) {
2333 underlay_xml_node = xml_node->add_child("Underlays");
2336 XMLNode* node = underlay_xml_node->add_child("Underlay");
2337 XMLProperty* prop = node->add_property("id");
2338 prop->set_value(v->trackview().route()->id().to_s());
2345 RouteTimeAxisView::remove_underlay (StreamView* v)
2351 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2352 RouteTimeAxisView& other = v->trackview();
2354 if (it != _underlay_streams.end()) {
2355 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2357 if (gm == other._underlay_mirrors.end()) {
2358 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2362 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2364 _underlay_streams.erase(it);
2365 other._underlay_mirrors.erase(gm);
2367 if (underlay_xml_node) {
2368 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2374 RouteTimeAxisView::set_button_names ()
2376 if (_route && _route->solo_safe()) {
2377 solo_button->remove ();
2378 if (solo_safe_pixbuf == 0) {
2379 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2381 solo_button->set_image (solo_safe_pixbuf);
2382 solo_button->set_text (string());
2384 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2385 if (Config->get_solo_control_is_listen_control()) {
2386 switch (Config->get_listen_position()) {
2387 case AfterFaderListen:
2388 solo_button->set_text (_("A"));
2389 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2391 case PreFaderListen:
2392 solo_button->set_text (_("P"));
2393 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2397 solo_button->set_text (_("s"));
2398 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2401 mute_button->set_text (_("m"));
2405 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2407 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2408 if (i != _main_automation_menu_map.end()) {
2412 i = _subplugin_menu_map.find (param);
2413 if (i != _subplugin_menu_map.end()) {
2421 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2423 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2425 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2429 gain_track.reset (new AutomationTimeAxisView (_session,
2430 _route, _route->amp(), c, param,
2435 _route->amp()->describe_parameter(param)));
2438 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2441 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2445 void add_region_to_list (RegionView* rv, RegionList* l)
2447 l->push_back (rv->region());
2451 RouteTimeAxisView::combine_regions ()
2453 /* as of may 2011, we do not offer uncombine for MIDI tracks
2456 if (!is_audio_track()) {
2464 RegionList selected_regions;
2465 boost::shared_ptr<Playlist> playlist = track()->playlist();
2467 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2469 if (selected_regions.size() < 2) {
2473 playlist->clear_changes ();
2474 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2476 _session->add_command (new StatefulDiffCommand (playlist));
2477 /* make the new region be selected */
2479 return _view->find_view (compound_region);
2483 RouteTimeAxisView::uncombine_regions ()
2485 /* as of may 2011, we do not offer uncombine for MIDI tracks
2487 if (!is_audio_track()) {
2495 RegionList selected_regions;
2496 boost::shared_ptr<Playlist> playlist = track()->playlist();
2498 /* have to grab selected regions first because the uncombine is going
2499 * to change that in the middle of the list traverse
2502 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2504 playlist->clear_changes ();
2506 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2507 playlist->uncombine (*i);
2510 _session->add_command (new StatefulDiffCommand (playlist));
2514 RouteTimeAxisView::state_id() const
2516 return string_compose ("rtav %1", _route->id().to_s());
2521 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2523 TimeAxisView::remove_child (c);
2525 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2527 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2528 if (i->second == a) {
2529 _automation_tracks.erase (i);