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_auditioner()) {
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 /* Attach the plugin submenu. It may have previously been used elsewhere,
391 so it was detached above
394 if (!subplugin_menu.items().empty()) {
395 items.push_back (SeparatorElem ());
396 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
397 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
402 RouteTimeAxisView::build_display_menu ()
404 using namespace Menu_Helpers;
408 TimeAxisView::build_display_menu ();
410 /* now fill it with our stuff */
412 MenuList& items = display_menu->items();
413 display_menu->set_name ("ArdourContextMenu");
415 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
418 detach_menu (*_size_menu);
421 items.push_back (MenuElem (_("Height"), *_size_menu));
423 items.push_back (SeparatorElem());
425 if (!Profile->get_sae()) {
426 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
427 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
428 items.push_back (SeparatorElem());
431 // Hook for derived classes to add type specific stuff
432 append_extra_display_menu_items ();
436 Menu* layers_menu = manage (new Menu);
437 MenuList &layers_items = layers_menu->items();
438 layers_menu->set_name("ArdourContextMenu");
440 RadioMenuItem::Group layers_group;
442 /* Find out how many overlaid/stacked tracks we have in the selection */
446 TrackSelection const & s = _editor.get_selection().tracks;
447 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
448 StreamView* v = (*i)->view ();
453 switch (v->layer_display ()) {
464 /* We're not connecting to signal_toggled() here; in the case where these two items are
465 set to be in the `inconsistent' state, it seems that one or other will end up active
466 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
467 select the active one, no toggled signal is emitted so nothing happens.
470 _ignore_set_layer_display = true;
472 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
473 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
474 i->set_active (overlaid != 0 && stacked == 0);
475 i->set_inconsistent (overlaid != 0 && stacked != 0);
476 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
478 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
479 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), Stacked, true));
484 _ignore_set_layer_display = false;
486 items.push_back (MenuElem (_("Layers"), *layers_menu));
488 if (!Profile->get_sae()) {
490 Menu* alignment_menu = manage (new Menu);
491 MenuList& alignment_items = alignment_menu->items();
492 alignment_menu->set_name ("ArdourContextMenu");
494 RadioMenuItem::Group align_group;
496 /* Same verbose hacks as for the layering options above */
502 boost::shared_ptr<Track> first_track;
504 TrackSelection const & s = _editor.get_selection().tracks;
505 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
506 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
507 if (!r || !r->is_track ()) {
512 first_track = r->track();
515 switch (r->track()->alignment_choice()) {
519 switch (r->track()->alignment_style()) {
520 case ExistingMaterial:
528 case UseExistingMaterial:
544 inconsistent = false;
553 if (!inconsistent && first_track) {
555 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
556 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
557 i->set_active (automatic != 0 && existing == 0 && capture == 0);
558 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
560 switch (first_track->alignment_choice()) {
562 switch (first_track->alignment_style()) {
563 case ExistingMaterial:
564 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
567 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
575 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
576 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
577 i->set_active (existing != 0 && capture == 0 && automatic == 0);
578 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
580 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
581 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
582 i->set_active (existing == 0 && capture != 0 && automatic == 0);
583 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
585 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
591 Menu* mode_menu = manage (new Menu);
592 MenuList& mode_items = mode_menu->items ();
593 mode_menu->set_name ("ArdourContextMenu");
595 RadioMenuItem::Group mode_group;
601 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
602 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
603 if (!r || !r->is_track ()) {
607 switch (r->track()->mode()) {
620 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
621 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
622 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
623 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
624 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
626 mode_items.push_back (RadioMenuElem (mode_group, _("Tape 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::Destructive, true));
629 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
630 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
632 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered 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::NonLayered, true));
635 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
636 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
638 items.push_back (MenuElem (_("Mode"), *mode_menu));
642 items.push_back (SeparatorElem());
644 build_playlist_menu ();
645 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
646 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
649 route_group_menu->detach ();
652 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
653 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
655 r.push_back (rtv->route ());
660 r.push_back (route ());
663 route_group_menu->build (r);
664 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
666 build_automation_action_menu (true);
667 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
669 items.push_back (SeparatorElem());
673 TrackSelection const & s = _editor.get_selection().tracks;
674 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
675 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
680 if (r->route()->active()) {
687 items.push_back (CheckMenuElem (_("Active")));
688 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
689 bool click_sets_active = true;
690 if (active > 0 && inactive == 0) {
691 i->set_active (true);
692 click_sets_active = false;
693 } else if (active > 0 && inactive > 0) {
694 i->set_inconsistent (true);
696 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
698 items.push_back (SeparatorElem());
699 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
700 if (!Profile->get_sae()) {
701 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
703 items.push_front (SeparatorElem());
704 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
709 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
711 if (apply_to_selection) {
712 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
717 if (!track()->can_use_mode (mode, needs_bounce)) {
723 cerr << "would bounce this one\n";
728 track()->set_mode (mode);
730 rec_enable_button->remove ();
733 case ARDOUR::NonLayered:
735 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
736 rec_enable_button->set_text (string());
738 case ARDOUR::Destructive:
739 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
740 rec_enable_button->set_text (string());
744 rec_enable_button->show_all ();
749 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
751 TimeAxisView::show_timestretch (start, end, layers, layer);
761 /* check that the time selection was made in our route, or our route group.
762 remember that route_group() == 0 implies the route is *not* in a edit group.
765 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
766 /* this doesn't apply to us */
770 /* ignore it if our edit group is not active */
772 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
777 if (timestretch_rect == 0) {
778 timestretch_rect = new SimpleRect (*canvas_display ());
779 timestretch_rect->property_x1() = 0.0;
780 timestretch_rect->property_y1() = 0.0;
781 timestretch_rect->property_x2() = 0.0;
782 timestretch_rect->property_y2() = 0.0;
783 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
784 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
787 timestretch_rect->show ();
788 timestretch_rect->raise_to_top ();
790 double const x1 = start / _editor.get_current_zoom();
791 double const x2 = (end - 1) / _editor.get_current_zoom();
793 timestretch_rect->property_x1() = x1;
794 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
795 timestretch_rect->property_x2() = x2;
796 timestretch_rect->property_y2() = 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);
834 gm.get_level_meter().setup_meters (gmlen);
836 TimeAxisView::set_height (h);
839 _view->set_height ((double) current_height());
842 if (height >= preset_height (HeightNormal)) {
846 gm.get_gain_slider().show();
848 if (!_route || _route->is_monitor()) {
853 if (rec_enable_button)
854 rec_enable_button->show();
856 route_group_button.show();
857 automation_button.show();
859 if (is_track() && track()->mode() == ARDOUR::Normal) {
860 playlist_button.show();
867 gm.get_gain_slider().hide();
869 if (!_route || _route->is_monitor()) {
874 if (rec_enable_button)
875 rec_enable_button->show();
877 route_group_button.hide ();
878 automation_button.hide ();
880 if (is_track() && track()->mode() == ARDOUR::Normal) {
881 playlist_button.hide ();
886 if (height_changed && !no_redraw) {
887 /* only emit the signal if the height really changed */
893 RouteTimeAxisView::route_color_changed ()
896 _view->apply_color (color(), StreamView::RegionColor);
901 RouteTimeAxisView::reset_samples_per_unit ()
903 set_samples_per_unit (_editor.get_current_zoom());
907 RouteTimeAxisView::horizontal_position_changed ()
910 _view->horizontal_position_changed ();
915 RouteTimeAxisView::set_samples_per_unit (double spu)
920 speed = track()->speed();
924 _view->set_samples_per_unit (spu * speed);
927 TimeAxisView::set_samples_per_unit (spu * speed);
931 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
933 if (!mitem->get_active()) {
934 /* this is one of the two calls made when these radio menu items change status. this one
935 is for the item that became inactive, and we want to ignore it.
940 if (apply_to_selection) {
941 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
944 track()->set_align_choice (choice);
950 RouteTimeAxisView::rename_current_playlist ()
952 ArdourPrompter prompter (true);
955 boost::shared_ptr<Track> tr = track();
956 if (!tr || tr->destructive()) {
960 boost::shared_ptr<Playlist> pl = tr->playlist();
965 prompter.set_title (_("Rename Playlist"));
966 prompter.set_prompt (_("New name for playlist:"));
967 prompter.set_initial_text (pl->name());
968 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
969 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
971 switch (prompter.run ()) {
972 case Gtk::RESPONSE_ACCEPT:
973 prompter.get_result (name);
985 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
987 std::string ret (basename);
989 std::string const group_string = "." + route_group()->name() + ".";
991 // iterate through all playlists
993 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
994 std::string tmp = (*i)->name();
996 std::string::size_type idx = tmp.find(group_string);
997 // find those which belong to this group
998 if (idx != string::npos) {
999 tmp = tmp.substr(idx + group_string.length());
1001 // and find the largest current number
1002 int x = atoi(tmp.c_str());
1003 if (x > maxnumber) {
1012 snprintf (buf, sizeof(buf), "%d", maxnumber);
1014 ret = this->name() + "." + route_group()->name () + "." + buf;
1020 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1024 boost::shared_ptr<Track> tr = track ();
1025 if (!tr || tr->destructive()) {
1029 boost::shared_ptr<const Playlist> pl = tr->playlist();
1036 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1037 name = resolve_new_group_playlist_name(name, playlists_before_op);
1040 while (_session->playlists->by_name(name)) {
1041 name = Playlist::bump_name (name, *_session);
1044 // TODO: The prompter "new" button should be de-activated if the user
1045 // specifies a playlist name which already exists in the session.
1049 ArdourPrompter prompter (true);
1051 prompter.set_title (_("New Copy Playlist"));
1052 prompter.set_prompt (_("Name for new playlist:"));
1053 prompter.set_initial_text (name);
1054 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1055 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1056 prompter.show_all ();
1058 switch (prompter.run ()) {
1059 case Gtk::RESPONSE_ACCEPT:
1060 prompter.get_result (name);
1068 if (name.length()) {
1069 tr->use_copy_playlist ();
1070 tr->playlist()->set_name (name);
1075 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1079 boost::shared_ptr<Track> tr = track ();
1080 if (!tr || tr->destructive()) {
1084 boost::shared_ptr<const Playlist> pl = tr->playlist();
1091 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1092 name = resolve_new_group_playlist_name(name,playlists_before_op);
1095 while (_session->playlists->by_name(name)) {
1096 name = Playlist::bump_name (name, *_session);
1102 ArdourPrompter prompter (true);
1104 prompter.set_title (_("New Playlist"));
1105 prompter.set_prompt (_("Name for new playlist:"));
1106 prompter.set_initial_text (name);
1107 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1108 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1110 switch (prompter.run ()) {
1111 case Gtk::RESPONSE_ACCEPT:
1112 prompter.get_result (name);
1120 if (name.length()) {
1121 tr->use_new_playlist ();
1122 tr->playlist()->set_name (name);
1127 RouteTimeAxisView::clear_playlist ()
1129 boost::shared_ptr<Track> tr = track ();
1130 if (!tr || tr->destructive()) {
1134 boost::shared_ptr<Playlist> pl = tr->playlist();
1139 _editor.clear_playlist (pl);
1143 RouteTimeAxisView::speed_changed ()
1145 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1149 RouteTimeAxisView::update_diskstream_display ()
1159 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1161 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1163 /* special case: select/deselect all tracks */
1164 if (_editor.get_selection().selected (this)) {
1165 _editor.get_selection().clear_tracks ();
1167 _editor.select_all_tracks ();
1173 switch (ArdourKeyboard::selection_type (ev->state)) {
1174 case Selection::Toggle:
1175 _editor.get_selection().toggle (this);
1178 case Selection::Set:
1179 _editor.get_selection().set (this);
1182 case Selection::Extend:
1183 _editor.extend_selection_to_track (*this);
1186 case Selection::Add:
1187 _editor.get_selection().add (this);
1193 RouteTimeAxisView::set_selected_points (PointSelection& points)
1195 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1196 (*i)->set_selected_points (points);
1201 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1204 _view->set_selected_regionviews (regions);
1208 /** Add the selectable things that we have to a list.
1209 * @param results List to add things to.
1212 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1217 speed = track()->speed();
1220 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1221 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1223 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1224 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1227 /* pick up visible automation tracks */
1229 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1230 if (!(*i)->hidden()) {
1231 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1237 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1240 _view->get_inverted_selectables (sel, results);
1243 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1244 if (!(*i)->hidden()) {
1245 (*i)->get_inverted_selectables (sel, results);
1253 RouteTimeAxisView::route_group () const
1255 return _route->route_group();
1259 RouteTimeAxisView::name() const
1261 return _route->name();
1264 boost::shared_ptr<Playlist>
1265 RouteTimeAxisView::playlist () const
1267 boost::shared_ptr<Track> tr;
1269 if ((tr = track()) != 0) {
1270 return tr->playlist();
1272 return boost::shared_ptr<Playlist> ();
1277 RouteTimeAxisView::name_entry_changed ()
1279 TimeAxisView::name_entry_changed ();
1281 string x = name_entry->get_text ();
1283 if (x == _route->name()) {
1287 strip_whitespace_edges (x);
1289 if (x.length() == 0) {
1290 name_entry->set_text (_route->name());
1294 if (_session->route_name_internal (x)) {
1295 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1297 name_entry->grab_focus ();
1298 } else if (RouteUI::verify_new_route_name (x)) {
1299 _route->set_name (x);
1301 name_entry->grab_focus ();
1305 boost::shared_ptr<Region>
1306 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1308 boost::shared_ptr<Playlist> pl = playlist ();
1311 return pl->find_next_region (pos, point, dir);
1314 return boost::shared_ptr<Region> ();
1318 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1320 boost::shared_ptr<Playlist> pl = playlist ();
1323 return pl->find_next_region_boundary (pos, dir);
1330 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1332 boost::shared_ptr<Playlist> what_we_got;
1333 boost::shared_ptr<Track> tr = track ();
1334 boost::shared_ptr<Playlist> playlist;
1337 /* route is a bus, not a track */
1341 playlist = tr->playlist();
1343 TimeSelection time (selection.time);
1344 float const speed = tr->speed();
1345 if (speed != 1.0f) {
1346 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1347 (*i).start = session_frame_to_track_frame((*i).start, speed);
1348 (*i).end = session_frame_to_track_frame((*i).end, speed);
1352 playlist->clear_changes ();
1353 playlist->clear_owned_changes ();
1357 if (playlist->cut (time) != 0) {
1358 vector<Command*> cmds;
1359 playlist->rdiff (cmds);
1360 _session->add_commands (cmds);
1362 _session->add_command (new StatefulDiffCommand (playlist));
1367 if ((what_we_got = playlist->cut (time)) != 0) {
1368 _editor.get_cut_buffer().add (what_we_got);
1369 vector<Command*> cmds;
1370 playlist->rdiff (cmds);
1371 _session->add_commands (cmds);
1373 _session->add_command (new StatefulDiffCommand (playlist));
1377 if ((what_we_got = playlist->copy (time)) != 0) {
1378 _editor.get_cut_buffer().add (what_we_got);
1383 if ((what_we_got = playlist->cut (time)) != 0) {
1385 vector<Command*> cmds;
1386 playlist->rdiff (cmds);
1387 _session->add_commands (cmds);
1388 _session->add_command (new StatefulDiffCommand (playlist));
1389 what_we_got->release ();
1396 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1402 boost::shared_ptr<Playlist> pl = playlist ();
1403 PlaylistSelection::iterator p;
1405 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1407 if (p == selection.playlists.end()) {
1411 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1413 if (track()->speed() != 1.0f) {
1414 pos = session_frame_to_track_frame (pos, track()->speed());
1415 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1418 pl->clear_changes ();
1419 pl->paste (*p, pos, times);
1420 _session->add_command (new StatefulDiffCommand (pl));
1426 struct PlaylistSorter {
1427 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1428 return a->sort_id() < b->sort_id();
1433 RouteTimeAxisView::build_playlist_menu ()
1435 using namespace Menu_Helpers;
1441 delete playlist_action_menu;
1442 playlist_action_menu = new Menu;
1443 playlist_action_menu->set_name ("ArdourContextMenu");
1445 MenuList& playlist_items = playlist_action_menu->items();
1446 playlist_action_menu->set_name ("ArdourContextMenu");
1447 playlist_items.clear();
1449 RadioMenuItem::Group playlist_group;
1450 boost::shared_ptr<Track> tr = track ();
1452 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1454 /* sort the playlists */
1456 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1458 /* add the playlists to the menu */
1459 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1460 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1461 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1462 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1464 if (tr->playlist()->id() == (*i)->id()) {
1470 playlist_items.push_back (SeparatorElem());
1471 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1472 playlist_items.push_back (SeparatorElem());
1474 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1475 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1476 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1479 // Use a label which tells the user what is happening
1480 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1481 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1485 playlist_items.push_back (SeparatorElem());
1486 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1487 playlist_items.push_back (SeparatorElem());
1489 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1493 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1495 assert (is_track());
1497 // exit if we were triggered by deactivating the old playlist
1498 if (!item->get_active()) {
1502 boost::shared_ptr<Playlist> pl (wpl.lock());
1508 if (track()->playlist() == pl) {
1509 // exit when use_playlist is called by the creation of the playlist menu
1510 // or the playlist choice is unchanged
1514 track()->use_playlist (pl);
1516 RouteGroup* rg = route_group();
1518 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1519 std::string group_string = "." + rg->name() + ".";
1521 std::string take_name = pl->name();
1522 std::string::size_type idx = take_name.find(group_string);
1524 if (idx == std::string::npos)
1527 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1529 boost::shared_ptr<RouteList> rl (rg->route_list());
1531 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1532 if ((*i) == this->route()) {
1536 std::string playlist_name = (*i)->name()+group_string+take_name;
1538 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1543 if (track->freeze_state() == Track::Frozen) {
1544 /* Don't change playlists of frozen tracks */
1548 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1550 // No playlist for this track for this take yet, make it
1551 track->use_new_playlist();
1552 track->playlist()->set_name(playlist_name);
1554 track->use_playlist(ipl);
1561 RouteTimeAxisView::update_playlist_tip ()
1563 RouteGroup* rg = route_group ();
1564 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1565 string group_string = "." + rg->name() + ".";
1567 string take_name = track()->playlist()->name();
1568 string::size_type idx = take_name.find(group_string);
1570 if (idx != string::npos) {
1571 /* find the bit containing the take number / name */
1572 take_name = take_name.substr (idx + group_string.length());
1574 /* set the playlist button tooltip to the take name */
1575 ARDOUR_UI::instance()->set_tip (
1577 string_compose(_("Take: %1.%2"),
1578 Glib::Markup::escape_text(rg->name()),
1579 Glib::Markup::escape_text(take_name))
1586 /* set the playlist button tooltip to the playlist name */
1587 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1592 RouteTimeAxisView::show_playlist_selector ()
1594 _editor.playlist_selector().show_for (this);
1598 RouteTimeAxisView::map_frozen ()
1604 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1606 switch (track()->freeze_state()) {
1608 playlist_button.set_sensitive (false);
1609 rec_enable_button->set_sensitive (false);
1612 playlist_button.set_sensitive (true);
1613 rec_enable_button->set_sensitive (true);
1619 RouteTimeAxisView::color_handler ()
1621 //case cTimeStretchOutline:
1622 if (timestretch_rect) {
1623 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1625 //case cTimeStretchFill:
1626 if (timestretch_rect) {
1627 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1633 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1634 * Will add track if necessary.
1637 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1639 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1640 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1643 /* it doesn't exist yet, so we don't care about the button state: just add it */
1644 create_automation_child (param, true);
1647 bool yn = menu->get_active();
1648 bool changed = false;
1650 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1652 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1653 will have done that for us.
1656 if (changed && !no_redraw) {
1664 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1666 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1672 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1674 if (menu && !_hidden) {
1675 ignore_toggle = true;
1676 menu->set_active (false);
1677 ignore_toggle = false;
1680 if (_route && !no_redraw) {
1687 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1689 if (apply_to_selection) {
1690 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1694 /* Show our automation */
1696 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1697 i->second->set_marked_for_display (true);
1699 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1702 menu->set_active(true);
1707 /* Show processor automation */
1709 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1710 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1711 if ((*ii)->view == 0) {
1712 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1715 (*ii)->menu_item->set_active (true);
1728 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1730 if (apply_to_selection) {
1731 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1735 /* Show our automation */
1737 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1738 if (i->second->has_automation()) {
1739 i->second->set_marked_for_display (true);
1741 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1743 menu->set_active(true);
1748 /* Show processor automation */
1750 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1751 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1752 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1753 (*ii)->menu_item->set_active (true);
1765 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1767 if (apply_to_selection) {
1768 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1772 /* Hide our automation */
1774 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1775 i->second->set_marked_for_display (false);
1777 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1780 menu->set_active (false);
1784 /* Hide processor automation */
1786 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1787 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1788 (*ii)->menu_item->set_active (false);
1799 RouteTimeAxisView::region_view_added (RegionView* rv)
1801 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1802 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1803 boost::shared_ptr<AutomationTimeAxisView> atv;
1805 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1810 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1811 (*i)->add_ghost(rv);
1815 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1817 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1823 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1825 parent.remove_processor_automation_node (this);
1829 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1832 remove_child (pan->view);
1836 RouteTimeAxisView::ProcessorAutomationNode*
1837 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1839 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1841 if ((*i)->processor == processor) {
1843 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1844 if ((*ii)->what == what) {
1854 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1856 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1859 ProcessorAutomationNode* pan;
1861 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1862 /* session state may never have been saved with new plugin */
1863 error << _("programming error: ")
1864 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1865 processor->name(), what.type(), (int) what.channel(), what.id() )
1875 boost::shared_ptr<AutomationControl> control
1876 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1878 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1879 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1880 _editor, *this, false, parent_canvas,
1881 processor->describe_parameter (what), processor->name()));
1883 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1885 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1888 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1893 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1896 pan->menu_item->set_active (false);
1905 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1907 boost::shared_ptr<Processor> processor (p.lock ());
1909 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1910 /* The Amp processor is a special case and is dealt with separately */
1914 set<Evoral::Parameter> existing;
1916 processor->what_has_data (existing);
1918 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1920 Evoral::Parameter param (*i);
1921 boost::shared_ptr<AutomationLine> al;
1923 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1926 add_processor_automation_curve (processor, param);
1932 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1934 using namespace Menu_Helpers;
1938 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1940 _automation_tracks[param] = track;
1942 /* existing state overrides "show" argument */
1943 string s = track->gui_property ("visible");
1945 show = string_is_affirmative (s);
1948 /* this might or might not change the visibility status, so don't rely on it */
1949 track->set_marked_for_display (show);
1951 if (show && !no_redraw) {
1955 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1956 /* MIDI-related parameters are always in the menu, there's no
1957 reason to rebuild the menu just because we added a automation
1958 lane for one of them. But if we add a non-MIDI automation
1959 lane, then we need to invalidate the display menu.
1961 delete display_menu;
1967 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1969 boost::shared_ptr<Processor> processor (p.lock ());
1971 if (!processor || !processor->display_to_user ()) {
1975 /* we use this override to veto the Amp processor from the plugin menu,
1976 as its automation lane can be accessed using the special "Fader" menu
1980 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1984 using namespace Menu_Helpers;
1985 ProcessorAutomationInfo *rai;
1986 list<ProcessorAutomationInfo*>::iterator x;
1988 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1990 if (automatable.empty()) {
1994 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1995 if ((*x)->processor == processor) {
2000 if (x == processor_automation.end()) {
2002 rai = new ProcessorAutomationInfo (processor);
2003 processor_automation.push_back (rai);
2011 /* any older menu was deleted at the top of processors_changed()
2012 when we cleared the subplugin menu.
2015 rai->menu = manage (new Menu);
2016 MenuList& items = rai->menu->items();
2017 rai->menu->set_name ("ArdourContextMenu");
2021 std::set<Evoral::Parameter> has_visible_automation;
2022 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2024 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2026 ProcessorAutomationNode* pan;
2027 CheckMenuItem* mitem;
2029 string name = processor->describe_parameter (*i);
2031 items.push_back (CheckMenuElem (name));
2032 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2034 _subplugin_menu_map[*i] = mitem;
2036 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2037 mitem->set_active(true);
2040 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2044 pan = new ProcessorAutomationNode (*i, mitem, *this);
2046 rai->lines.push_back (pan);
2050 pan->menu_item = mitem;
2054 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2057 /* add the menu for this processor, because the subplugin
2058 menu is always cleared at the top of processors_changed().
2059 this is the result of some poor design in gtkmm and/or
2063 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2068 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2069 RouteTimeAxisView::ProcessorAutomationNode* pan)
2071 bool showit = pan->menu_item->get_active();
2072 bool redraw = false;
2074 if (pan->view == 0 && showit) {
2075 add_processor_automation_curve (rai->processor, pan->what);
2079 if (pan->view && pan->view->set_marked_for_display (showit)) {
2083 if (redraw && !no_redraw) {
2089 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2091 if (c.type == RouteProcessorChange::MeterPointChange) {
2092 /* nothing to do if only the meter point has changed */
2096 using namespace Menu_Helpers;
2098 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2099 (*i)->valid = false;
2102 setup_processor_menu_and_curves ();
2104 bool deleted_processor_automation = false;
2106 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2108 list<ProcessorAutomationInfo*>::iterator tmp;
2116 processor_automation.erase (i);
2117 deleted_processor_automation = true;
2124 if (deleted_processor_automation && !no_redraw) {
2129 boost::shared_ptr<AutomationLine>
2130 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2132 ProcessorAutomationNode* pan;
2134 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2140 return boost::shared_ptr<AutomationLine>();
2144 RouteTimeAxisView::reset_processor_automation_curves ()
2146 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2152 RouteTimeAxisView::can_edit_name () const
2154 /* we do not allow track name changes if it is record enabled
2156 return !_route->record_enabled();
2160 RouteTimeAxisView::update_rec_display ()
2162 RouteUI::update_rec_display ();
2166 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2168 if (_ignore_set_layer_display) {
2172 if (apply_to_selection) {
2173 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2177 _view->set_layer_display (d);
2180 set_gui_property (X_("layer-display"), enum_2_string (d));
2185 RouteTimeAxisView::layer_display () const
2188 return _view->layer_display ();
2191 /* we don't know, since we don't have a _view, so just return something */
2197 boost::shared_ptr<AutomationTimeAxisView>
2198 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2200 AutomationTracks::iterator i = _automation_tracks.find(param);
2201 if (i != _automation_tracks.end()) {
2204 return boost::shared_ptr<AutomationTimeAxisView>();
2209 RouteTimeAxisView::fast_update ()
2211 gm.get_level_meter().update_meters ();
2215 RouteTimeAxisView::hide_meter ()
2218 gm.get_level_meter().hide_meters ();
2222 RouteTimeAxisView::show_meter ()
2228 RouteTimeAxisView::reset_meter ()
2230 if (Config->get_show_track_meters()) {
2231 gm.get_level_meter().setup_meters (height-5);
2238 RouteTimeAxisView::clear_meter ()
2240 gm.get_level_meter().clear_meters ();
2244 RouteTimeAxisView::meter_changed ()
2246 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2251 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2257 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2259 using namespace Menu_Helpers;
2261 if (!_underlay_streams.empty()) {
2262 MenuList& parent_items = parent_menu->items();
2263 Menu* gs_menu = manage (new Menu);
2264 gs_menu->set_name ("ArdourContextMenu");
2265 MenuList& gs_items = gs_menu->items();
2267 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2269 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2270 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2271 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2277 RouteTimeAxisView::set_underlay_state()
2279 if (!underlay_xml_node) {
2283 XMLNodeList nlist = underlay_xml_node->children();
2284 XMLNodeConstIterator niter;
2285 XMLNode *child_node;
2287 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2288 child_node = *niter;
2290 if (child_node->name() != "Underlay") {
2294 XMLProperty* prop = child_node->property ("id");
2296 PBD::ID id (prop->value());
2298 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2301 add_underlay(v->view(), false);
2310 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2316 RouteTimeAxisView& other = v->trackview();
2318 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2319 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2320 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2324 _underlay_streams.push_back(v);
2325 other._underlay_mirrors.push_back(this);
2327 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2329 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2331 if (!underlay_xml_node) {
2332 underlay_xml_node = xml_node->add_child("Underlays");
2335 XMLNode* node = underlay_xml_node->add_child("Underlay");
2336 XMLProperty* prop = node->add_property("id");
2337 prop->set_value(v->trackview().route()->id().to_s());
2344 RouteTimeAxisView::remove_underlay (StreamView* v)
2350 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2351 RouteTimeAxisView& other = v->trackview();
2353 if (it != _underlay_streams.end()) {
2354 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2356 if (gm == other._underlay_mirrors.end()) {
2357 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2361 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2363 _underlay_streams.erase(it);
2364 other._underlay_mirrors.erase(gm);
2366 if (underlay_xml_node) {
2367 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2373 RouteTimeAxisView::set_button_names ()
2375 if (_route && _route->solo_safe()) {
2376 solo_button->remove ();
2377 if (solo_safe_pixbuf == 0) {
2378 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2380 solo_button->set_image (solo_safe_pixbuf);
2381 solo_button->set_text (string());
2383 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2384 if (Config->get_solo_control_is_listen_control()) {
2385 switch (Config->get_listen_position()) {
2386 case AfterFaderListen:
2387 solo_button->set_text (_("A"));
2388 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2390 case PreFaderListen:
2391 solo_button->set_text (_("P"));
2392 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2396 solo_button->set_text (_("s"));
2397 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2400 mute_button->set_text (_("m"));
2404 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2406 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2407 if (i != _main_automation_menu_map.end()) {
2411 i = _subplugin_menu_map.find (param);
2412 if (i != _subplugin_menu_map.end()) {
2420 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2422 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2424 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2428 gain_track.reset (new AutomationTimeAxisView (_session,
2429 _route, _route->amp(), c, param,
2434 _route->amp()->describe_parameter(param)));
2437 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2440 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2444 void add_region_to_list (RegionView* rv, RegionList* l)
2446 l->push_back (rv->region());
2450 RouteTimeAxisView::combine_regions ()
2452 /* as of may 2011, we do not offer uncombine for MIDI tracks
2455 if (!is_audio_track()) {
2463 RegionList selected_regions;
2464 boost::shared_ptr<Playlist> playlist = track()->playlist();
2466 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2468 if (selected_regions.size() < 2) {
2472 playlist->clear_changes ();
2473 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2475 _session->add_command (new StatefulDiffCommand (playlist));
2476 /* make the new region be selected */
2478 return _view->find_view (compound_region);
2482 RouteTimeAxisView::uncombine_regions ()
2484 /* as of may 2011, we do not offer uncombine for MIDI tracks
2486 if (!is_audio_track()) {
2494 RegionList selected_regions;
2495 boost::shared_ptr<Playlist> playlist = track()->playlist();
2497 /* have to grab selected regions first because the uncombine is going
2498 * to change that in the middle of the list traverse
2501 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2503 playlist->clear_changes ();
2505 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2506 playlist->uncombine (*i);
2509 _session->add_command (new StatefulDiffCommand (playlist));
2513 RouteTimeAxisView::state_id() const
2515 return string_compose ("rtav %1", _route->id().to_s());
2520 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2522 TimeAxisView::remove_child (c);
2524 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2526 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2527 if (i->second == a) {
2528 _automation_tracks.erase (i);