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/audioplaylist.h"
47 #include "ardour/diskstream.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/ladspa_plugin.h"
50 #include "ardour/location.h"
51 #include "ardour/panner.h"
52 #include "ardour/playlist.h"
53 #include "ardour/playlist.h"
54 #include "ardour/processor.h"
55 #include "ardour/profile.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
65 #include "ardour_button.h"
67 #include "global_signals.h"
68 #include "route_time_axis.h"
69 #include "automation_time_axis.h"
70 #include "canvas_impl.h"
71 #include "crossfade_view.h"
73 #include "gui_thread.h"
75 #include "playlist_selector.h"
76 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "simplerect.h"
83 #include "streamview.h"
85 #include "route_group_menu.h"
87 #include "ardour/track.h"
91 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
95 using namespace Editing;
99 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
102 RouteTimeAxisView::setup_slider_pix ()
104 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
105 throw failed_constructor ();
109 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
112 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
114 , parent_canvas (canvas)
116 , button_table (3, 3)
117 , route_group_button (_("g"))
118 , playlist_button (_("p"))
119 , automation_button (_("a"))
120 , automation_action_menu (0)
121 , plugins_submenu_item (0)
122 , route_group_menu (0)
123 , playlist_action_menu (0)
125 , color_mode_menu (0)
126 , gm (sess, slider, true, 115)
131 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
133 RouteUI::set_route (rt);
135 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
136 gm.get_level_meter().set_no_show_all();
137 gm.get_level_meter().setup_meters(50);
139 string str = gui_property ("height");
141 set_height (atoi (str));
143 set_height (preset_height (HeightNormal));
146 if (!_route->is_hidden()) {
147 if (gui_property ("visible").empty()) {
148 set_gui_property ("visible", true);
151 set_gui_property ("visible", false);
155 update_solo_display ();
157 timestretch_rect = 0;
160 ignore_toggle = false;
162 route_group_button.set_name ("TrackGroupButton");
163 playlist_button.set_name ("TrackPlaylistButton");
164 automation_button.set_name ("TrackAutomationButton");
166 route_group_button.unset_flags (Gtk::CAN_FOCUS);
167 playlist_button.unset_flags (Gtk::CAN_FOCUS);
168 automation_button.unset_flags (Gtk::CAN_FOCUS);
170 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
171 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
172 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
178 switch (track()->mode()) {
180 case ARDOUR::NonLayered:
181 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
183 case ARDOUR::Destructive:
184 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
188 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
190 if (is_midi_track()) {
191 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
193 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
196 rec_enable_button->set_sensitive (_session->writable());
199 controls_hbox.pack_start(gm.get_level_meter(), false, false);
200 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
201 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
202 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 if (!_route->is_master()) {
207 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
210 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
211 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
214 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
215 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
216 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
217 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
221 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
223 if (is_track() && track()->mode() == ARDOUR::Normal) {
224 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
229 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
230 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
234 str = gui_property ("layer-display");
236 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
239 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
240 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
242 /* pick up the correct freeze state */
246 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
247 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
248 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
250 PropertyList* plist = new PropertyList();
252 plist->add (ARDOUR::Properties::edit, true);
253 plist->add (ARDOUR::Properties::mute, true);
254 plist->add (ARDOUR::Properties::solo, true);
256 route_group_menu = new RouteGroupMenu (_session, plist);
258 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
259 gm.get_gain_slider().set_name ("TrackGainFader");
261 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
267 RouteTimeAxisView::~RouteTimeAxisView ()
269 CatchDeletion (this);
271 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
275 delete playlist_action_menu;
276 playlist_action_menu = 0;
281 _automation_tracks.clear ();
283 delete route_group_menu;
287 RouteTimeAxisView::post_construct ()
289 /* map current state of the route */
291 update_diskstream_display ();
292 setup_processor_menu_and_curves ();
293 reset_processor_automation_curves ();
296 /** Set up the processor menu for the current set of processors, and
297 * display automation curves for any parameters which have data.
300 RouteTimeAxisView::setup_processor_menu_and_curves ()
302 _subplugin_menu_map.clear ();
303 subplugin_menu.items().clear ();
304 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
305 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
309 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
311 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
312 if (_route->route_group()) {
313 _route->route_group()->remove (_route);
319 r.push_back (route ());
321 route_group_menu->build (r);
322 route_group_menu->menu()->popup (ev->button, ev->time);
328 RouteTimeAxisView::playlist_changed ()
334 RouteTimeAxisView::label_view ()
336 string x = _route->name();
338 if (x != name_entry.get_text()) {
339 name_entry.set_text (x);
342 if (x != name_label.get_text()) {
343 name_label.set_text (x);
346 ARDOUR_UI::instance()->set_tip (name_entry, x);
350 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
352 if (what_changed.contains (ARDOUR::Properties::name)) {
358 RouteTimeAxisView::take_name_changed (void *src)
366 RouteTimeAxisView::playlist_click ()
368 build_playlist_menu ();
369 conditionally_add_to_selection ();
370 playlist_action_menu->popup (1, gtk_get_current_event_time());
374 RouteTimeAxisView::automation_click ()
376 conditionally_add_to_selection ();
377 build_automation_action_menu (false);
378 automation_action_menu->popup (1, gtk_get_current_event_time());
382 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
384 using namespace Menu_Helpers;
386 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
387 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
390 detach_menu (subplugin_menu);
392 _main_automation_menu_map.clear ();
393 delete automation_action_menu;
394 automation_action_menu = new Menu;
396 MenuList& items = automation_action_menu->items();
398 automation_action_menu->set_name ("ArdourContextMenu");
400 items.push_back (MenuElem (_("Show All Automation"),
401 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
403 items.push_back (MenuElem (_("Show Existing Automation"),
404 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
406 items.push_back (MenuElem (_("Hide All Automation"),
407 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
409 items.push_back (SeparatorElem ());
411 /* Attach the plugin submenu. It may have previously been used elsewhere,
412 so it was detached above */
414 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
415 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
419 RouteTimeAxisView::build_display_menu ()
421 using namespace Menu_Helpers;
425 TimeAxisView::build_display_menu ();
427 /* now fill it with our stuff */
429 MenuList& items = display_menu->items();
430 display_menu->set_name ("ArdourContextMenu");
432 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
435 detach_menu (*_size_menu);
438 items.push_back (MenuElem (_("Height"), *_size_menu));
440 items.push_back (SeparatorElem());
442 if (!Profile->get_sae()) {
443 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
444 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
445 items.push_back (SeparatorElem());
448 // Hook for derived classes to add type specific stuff
449 append_extra_display_menu_items ();
453 Menu* layers_menu = manage (new Menu);
454 MenuList &layers_items = layers_menu->items();
455 layers_menu->set_name("ArdourContextMenu");
457 RadioMenuItem::Group layers_group;
459 /* Find out how many overlaid/stacked tracks we have in the selection */
463 TrackSelection const & s = _editor.get_selection().tracks;
464 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
465 StreamView* v = (*i)->view ();
470 switch (v->layer_display ()) {
480 /* We're not connecting to signal_toggled() here; in the case where these two items are
481 set to be in the `inconsistent' state, it seems that one or other will end up active
482 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
483 select the active one, no toggled signal is emitted so nothing happens.
486 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
487 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
488 i->set_active (overlaid != 0 && stacked == 0);
489 i->set_inconsistent (overlaid != 0 && stacked != 0);
490 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
492 layers_items.push_back (
493 RadioMenuElem (layers_group, _("Stacked"),
494 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
497 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
498 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
499 i->set_active (overlaid == 0 && stacked != 0);
500 i->set_inconsistent (overlaid != 0 && stacked != 0);
502 items.push_back (MenuElem (_("Layers"), *layers_menu));
504 if (!Profile->get_sae()) {
506 Menu* alignment_menu = manage (new Menu);
507 MenuList& alignment_items = alignment_menu->items();
508 alignment_menu->set_name ("ArdourContextMenu");
510 RadioMenuItem::Group align_group;
512 /* Same verbose hacks as for the layering options above */
518 boost::shared_ptr<Track> first_track;
520 TrackSelection const & s = _editor.get_selection().tracks;
521 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
522 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
523 if (!r || !r->is_track ()) {
528 first_track = r->track();
531 switch (r->track()->alignment_choice()) {
535 switch (r->track()->alignment_style()) {
536 case ExistingMaterial:
544 case UseExistingMaterial:
560 inconsistent = false;
569 if (!inconsistent && first_track) {
571 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
572 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
573 i->set_active (automatic != 0 && existing == 0 && capture == 0);
574 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
576 switch (first_track->alignment_choice()) {
578 switch (first_track->alignment_style()) {
579 case ExistingMaterial:
580 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
583 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
591 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
592 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
593 i->set_active (existing != 0 && capture == 0 && automatic == 0);
594 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
596 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
597 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
598 i->set_active (existing == 0 && capture != 0 && automatic == 0);
599 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
601 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
607 Menu* mode_menu = manage (new Menu);
608 MenuList& mode_items = mode_menu->items ();
609 mode_menu->set_name ("ArdourContextMenu");
611 RadioMenuItem::Group mode_group;
617 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
618 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
619 if (!r || !r->is_track ()) {
623 switch (r->track()->mode()) {
636 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
637 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
638 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
639 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
640 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
642 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
643 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
644 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
645 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
646 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
648 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
649 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
650 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
651 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
652 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
654 items.push_back (MenuElem (_("Mode"), *mode_menu));
657 color_mode_menu = build_color_mode_menu();
658 if (color_mode_menu) {
659 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
662 items.push_back (SeparatorElem());
664 build_playlist_menu ();
665 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
666 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
668 route_group_menu->detach ();
671 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
672 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
674 r.push_back (rtv->route ());
679 r.push_back (route ());
682 route_group_menu->build (r);
683 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
685 build_automation_action_menu (true);
686 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
688 items.push_back (SeparatorElem());
693 TrackSelection const & s = _editor.get_selection().tracks;
694 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
695 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
700 if (r->route()->active()) {
707 items.push_back (CheckMenuElem (_("Active")));
708 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
709 bool click_sets_active = true;
710 if (active > 0 && inactive == 0) {
711 i->set_active (true);
712 click_sets_active = false;
713 } else if (active > 0 && inactive > 0) {
714 i->set_inconsistent (true);
716 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
718 items.push_back (SeparatorElem());
719 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
720 if (!Profile->get_sae()) {
721 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
723 items.push_front (SeparatorElem());
724 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
729 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
731 if (apply_to_selection) {
732 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
737 if (!track()->can_use_mode (mode, needs_bounce)) {
743 cerr << "would bounce this one\n";
748 track()->set_mode (mode);
750 rec_enable_button->remove ();
753 case ARDOUR::NonLayered:
755 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
756 rec_enable_button->set_text (string());
758 case ARDOUR::Destructive:
759 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
760 rec_enable_button->set_text (string());
764 rec_enable_button->show_all ();
769 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
775 TimeAxisView::show_timestretch (start, end);
785 /* check that the time selection was made in our route, or our route group.
786 remember that route_group() == 0 implies the route is *not* in a edit group.
789 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
790 /* this doesn't apply to us */
794 /* ignore it if our edit group is not active */
796 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
801 if (timestretch_rect == 0) {
802 timestretch_rect = new SimpleRect (*canvas_display ());
803 timestretch_rect->property_x1() = 0.0;
804 timestretch_rect->property_y1() = 0.0;
805 timestretch_rect->property_x2() = 0.0;
806 timestretch_rect->property_y2() = 0.0;
807 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
808 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
811 timestretch_rect->show ();
812 timestretch_rect->raise_to_top ();
814 x1 = start / _editor.get_current_zoom();
815 x2 = (end - 1) / _editor.get_current_zoom();
816 y2 = current_height() - 2;
818 timestretch_rect->property_x1() = x1;
819 timestretch_rect->property_y1() = 1.0;
820 timestretch_rect->property_x2() = x2;
821 timestretch_rect->property_y2() = y2;
825 RouteTimeAxisView::hide_timestretch ()
827 TimeAxisView::hide_timestretch ();
829 if (timestretch_rect) {
830 timestretch_rect->hide ();
835 RouteTimeAxisView::show_selection (TimeSelection& ts)
839 /* ignore it if our edit group is not active or if the selection was started
840 in some other track or route group (remember that route_group() == 0 means
841 that the track is not in an route group).
844 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
845 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
851 TimeAxisView::show_selection (ts);
855 RouteTimeAxisView::set_height (uint32_t h)
858 bool height_changed = (height == 0) || (h != height);
859 gm.get_level_meter().setup_meters (gmlen);
861 TimeAxisView::set_height (h);
864 _view->set_height ((double) current_height());
867 if (height >= preset_height (HeightNormal)) {
871 gm.get_gain_slider().show();
873 if (!_route || _route->is_monitor()) {
878 if (rec_enable_button)
879 rec_enable_button->show();
881 route_group_button.show();
882 automation_button.show();
884 if (is_track() && track()->mode() == ARDOUR::Normal) {
885 playlist_button.show();
892 gm.get_gain_slider().hide();
894 if (!_route || _route->is_monitor()) {
899 if (rec_enable_button)
900 rec_enable_button->show();
902 route_group_button.hide ();
903 automation_button.hide ();
905 if (is_track() && track()->mode() == ARDOUR::Normal) {
906 playlist_button.hide ();
911 if (height_changed && !no_redraw) {
912 /* only emit the signal if the height really changed */
918 RouteTimeAxisView::route_color_changed ()
921 _view->apply_color (color(), StreamView::RegionColor);
926 RouteTimeAxisView::reset_samples_per_unit ()
928 set_samples_per_unit (_editor.get_current_zoom());
932 RouteTimeAxisView::horizontal_position_changed ()
935 _view->horizontal_position_changed ();
940 RouteTimeAxisView::set_samples_per_unit (double spu)
945 speed = track()->speed();
949 _view->set_samples_per_unit (spu * speed);
952 TimeAxisView::set_samples_per_unit (spu * speed);
956 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
958 if (!mitem->get_active()) {
959 /* this is one of the two calls made when these radio menu items change status. this one
960 is for the item that became inactive, and we want to ignore it.
965 if (apply_to_selection) {
966 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
969 track()->set_align_choice (choice);
975 RouteTimeAxisView::rename_current_playlist ()
977 ArdourPrompter prompter (true);
980 boost::shared_ptr<Track> tr = track();
981 if (!tr || tr->destructive()) {
985 boost::shared_ptr<Playlist> pl = tr->playlist();
990 prompter.set_title (_("Rename Playlist"));
991 prompter.set_prompt (_("New name for playlist:"));
992 prompter.set_initial_text (pl->name());
993 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
994 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
996 switch (prompter.run ()) {
997 case Gtk::RESPONSE_ACCEPT:
998 prompter.get_result (name);
1000 pl->set_name (name);
1010 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1012 std::string ret (basename);
1014 std::string const group_string = "." + route_group()->name() + ".";
1016 // iterate through all playlists
1018 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1019 std::string tmp = (*i)->name();
1021 std::string::size_type idx = tmp.find(group_string);
1022 // find those which belong to this group
1023 if (idx != string::npos) {
1024 tmp = tmp.substr(idx + group_string.length());
1026 // and find the largest current number
1027 int x = atoi(tmp.c_str());
1028 if (x > maxnumber) {
1037 snprintf (buf, sizeof(buf), "%d", maxnumber);
1039 ret = this->name() + "." + route_group()->name () + "." + buf;
1045 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1049 boost::shared_ptr<Track> tr = track ();
1050 if (!tr || tr->destructive()) {
1054 boost::shared_ptr<const Playlist> pl = tr->playlist();
1061 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1062 name = resolve_new_group_playlist_name(name, playlists_before_op);
1065 while (_session->playlists->by_name(name)) {
1066 name = Playlist::bump_name (name, *_session);
1069 // TODO: The prompter "new" button should be de-activated if the user
1070 // specifies a playlist name which already exists in the session.
1074 ArdourPrompter prompter (true);
1076 prompter.set_title (_("New Copy Playlist"));
1077 prompter.set_prompt (_("Name for new playlist:"));
1078 prompter.set_initial_text (name);
1079 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1080 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1081 prompter.show_all ();
1083 switch (prompter.run ()) {
1084 case Gtk::RESPONSE_ACCEPT:
1085 prompter.get_result (name);
1093 if (name.length()) {
1094 tr->use_copy_playlist ();
1095 tr->playlist()->set_name (name);
1100 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1104 boost::shared_ptr<Track> tr = track ();
1105 if (!tr || tr->destructive()) {
1109 boost::shared_ptr<const Playlist> pl = tr->playlist();
1116 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1117 name = resolve_new_group_playlist_name(name,playlists_before_op);
1120 while (_session->playlists->by_name(name)) {
1121 name = Playlist::bump_name (name, *_session);
1127 ArdourPrompter prompter (true);
1129 prompter.set_title (_("New Playlist"));
1130 prompter.set_prompt (_("Name for new playlist:"));
1131 prompter.set_initial_text (name);
1132 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1133 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1135 switch (prompter.run ()) {
1136 case Gtk::RESPONSE_ACCEPT:
1137 prompter.get_result (name);
1145 if (name.length()) {
1146 tr->use_new_playlist ();
1147 tr->playlist()->set_name (name);
1152 RouteTimeAxisView::clear_playlist ()
1154 boost::shared_ptr<Track> tr = track ();
1155 if (!tr || tr->destructive()) {
1159 boost::shared_ptr<Playlist> pl = tr->playlist();
1164 _editor.clear_playlist (pl);
1168 RouteTimeAxisView::speed_changed ()
1170 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1174 RouteTimeAxisView::update_diskstream_display ()
1184 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1186 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1188 /* special case: select/deselect all tracks */
1189 if (_editor.get_selection().selected (this)) {
1190 _editor.get_selection().clear_tracks ();
1192 _editor.select_all_tracks ();
1198 switch (ArdourKeyboard::selection_type (ev->state)) {
1199 case Selection::Toggle:
1200 _editor.get_selection().toggle (this);
1203 case Selection::Set:
1204 _editor.get_selection().set (this);
1207 case Selection::Extend:
1208 _editor.extend_selection_to_track (*this);
1211 case Selection::Add:
1212 _editor.get_selection().add (this);
1218 RouteTimeAxisView::set_selected_points (PointSelection& points)
1220 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1221 (*i)->set_selected_points (points);
1226 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1229 _view->set_selected_regionviews (regions);
1233 /** Add the selectable things that we have to a list.
1234 * @param results List to add things to.
1237 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1242 speed = track()->speed();
1245 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1246 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1248 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1249 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1252 /* pick up visible automation tracks */
1254 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1255 if (!(*i)->hidden()) {
1256 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1262 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1265 _view->get_inverted_selectables (sel, results);
1268 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1269 if (!(*i)->hidden()) {
1270 (*i)->get_inverted_selectables (sel, results);
1278 RouteTimeAxisView::route_group () const
1280 return _route->route_group();
1284 RouteTimeAxisView::name() const
1286 return _route->name();
1289 boost::shared_ptr<Playlist>
1290 RouteTimeAxisView::playlist () const
1292 boost::shared_ptr<Track> tr;
1294 if ((tr = track()) != 0) {
1295 return tr->playlist();
1297 return boost::shared_ptr<Playlist> ();
1302 RouteTimeAxisView::name_entry_changed ()
1304 string x = name_entry.get_text ();
1306 if (x == _route->name()) {
1310 strip_whitespace_edges (x);
1312 if (x.length() == 0) {
1313 name_entry.set_text (_route->name());
1317 if (_session->route_name_internal (x)) {
1318 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1320 name_entry.grab_focus ();
1321 } else if (RouteUI::verify_new_route_name (x)) {
1322 _route->set_name (x);
1324 name_entry.grab_focus ();
1328 boost::shared_ptr<Region>
1329 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1331 boost::shared_ptr<Playlist> pl = playlist ();
1334 return pl->find_next_region (pos, point, dir);
1337 return boost::shared_ptr<Region> ();
1341 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1343 boost::shared_ptr<Playlist> pl = playlist ();
1346 return pl->find_next_region_boundary (pos, dir);
1353 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1355 boost::shared_ptr<Playlist> what_we_got;
1356 boost::shared_ptr<Track> tr = track ();
1357 boost::shared_ptr<Playlist> playlist;
1360 /* route is a bus, not a track */
1364 playlist = tr->playlist();
1366 TimeSelection time (selection.time);
1367 float const speed = tr->speed();
1368 if (speed != 1.0f) {
1369 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1370 (*i).start = session_frame_to_track_frame((*i).start, speed);
1371 (*i).end = session_frame_to_track_frame((*i).end, speed);
1375 playlist->clear_changes ();
1376 playlist->clear_owned_changes ();
1380 if (playlist->cut (time) != 0) {
1381 vector<Command*> cmds;
1382 playlist->rdiff (cmds);
1383 _session->add_commands (cmds);
1385 _session->add_command (new StatefulDiffCommand (playlist));
1390 if ((what_we_got = playlist->cut (time)) != 0) {
1391 _editor.get_cut_buffer().add (what_we_got);
1392 vector<Command*> cmds;
1393 playlist->rdiff (cmds);
1394 _session->add_commands (cmds);
1396 _session->add_command (new StatefulDiffCommand (playlist));
1400 if ((what_we_got = playlist->copy (time)) != 0) {
1401 _editor.get_cut_buffer().add (what_we_got);
1406 if ((what_we_got = playlist->cut (time)) != 0) {
1408 vector<Command*> cmds;
1409 playlist->rdiff (cmds);
1410 _session->add_commands (cmds);
1411 _session->add_command (new StatefulDiffCommand (playlist));
1412 what_we_got->release ();
1419 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1425 boost::shared_ptr<Playlist> pl = playlist ();
1426 PlaylistSelection::iterator p;
1428 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1430 if (p == selection.playlists.end()) {
1434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1436 if (track()->speed() != 1.0f) {
1437 pos = session_frame_to_track_frame (pos, track()->speed());
1438 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1441 pl->clear_changes ();
1442 pl->paste (*p, pos, times);
1443 _session->add_command (new StatefulDiffCommand (pl));
1449 struct PlaylistSorter {
1450 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1451 return a->sort_id() < b->sort_id();
1456 RouteTimeAxisView::build_playlist_menu ()
1458 using namespace Menu_Helpers;
1464 delete playlist_action_menu;
1465 playlist_action_menu = new Menu;
1466 playlist_action_menu->set_name ("ArdourContextMenu");
1468 MenuList& playlist_items = playlist_action_menu->items();
1469 playlist_action_menu->set_name ("ArdourContextMenu");
1470 playlist_items.clear();
1472 RadioMenuItem::Group playlist_group;
1473 boost::shared_ptr<Track> tr = track ();
1475 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1477 /* sort the playlists */
1479 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1481 /* add the playlists to the menu */
1482 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1483 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1484 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1485 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1487 if (tr->playlist()->id() == (*i)->id()) {
1493 playlist_items.push_back (SeparatorElem());
1494 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1495 playlist_items.push_back (SeparatorElem());
1497 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1498 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1499 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1502 // Use a label which tells the user what is happening
1503 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1504 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1508 playlist_items.push_back (SeparatorElem());
1509 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1510 playlist_items.push_back (SeparatorElem());
1512 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1516 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1518 assert (is_track());
1520 // exit if we were triggered by deactivating the old playlist
1521 if (!item->get_active()) {
1525 boost::shared_ptr<Playlist> pl (wpl.lock());
1531 if (track()->playlist() == pl) {
1532 // exit when use_playlist is called by the creation of the playlist menu
1533 // or the playlist choice is unchanged
1537 track()->use_playlist (pl);
1539 RouteGroup* rg = route_group();
1541 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1542 std::string group_string = "." + rg->name() + ".";
1544 std::string take_name = pl->name();
1545 std::string::size_type idx = take_name.find(group_string);
1547 if (idx == std::string::npos)
1550 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1552 boost::shared_ptr<RouteList> rl (rg->route_list());
1554 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1555 if ( (*i) == this->route()) {
1559 std::string playlist_name = (*i)->name()+group_string+take_name;
1561 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1566 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1568 // No playlist for this track for this take yet, make it
1569 track->use_new_playlist();
1570 track->playlist()->set_name(playlist_name);
1572 track->use_playlist(ipl);
1579 RouteTimeAxisView::show_playlist_selector ()
1581 _editor.playlist_selector().show_for (this);
1585 RouteTimeAxisView::map_frozen ()
1591 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1593 switch (track()->freeze_state()) {
1595 playlist_button.set_sensitive (false);
1596 rec_enable_button->set_sensitive (false);
1599 playlist_button.set_sensitive (true);
1600 rec_enable_button->set_sensitive (true);
1606 RouteTimeAxisView::color_handler ()
1608 //case cTimeStretchOutline:
1609 if (timestretch_rect) {
1610 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1612 //case cTimeStretchFill:
1613 if (timestretch_rect) {
1614 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1620 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1621 * Will add track if necessary.
1624 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1626 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1627 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1630 /* it doesn't exist yet, so we don't care about the button state: just add it */
1631 create_automation_child (param, true);
1634 bool yn = menu->get_active();
1635 bool changed = false;
1637 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1639 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1640 will have done that for us.
1643 if (changed && !no_redraw) {
1651 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1653 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1659 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1661 if (menu && !_hidden) {
1662 ignore_toggle = true;
1663 menu->set_active (false);
1664 ignore_toggle = false;
1667 if (_route && !no_redraw) {
1674 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1676 if (apply_to_selection) {
1677 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1681 /* Show our automation */
1683 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1684 i->second->set_marked_for_display (true);
1686 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1689 menu->set_active(true);
1694 /* Show processor automation */
1696 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1697 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1698 if ((*ii)->view == 0) {
1699 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1702 (*ii)->menu_item->set_active (true);
1715 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1717 if (apply_to_selection) {
1718 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1722 /* Show our automation */
1724 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1725 if (i->second->has_automation()) {
1726 i->second->set_marked_for_display (true);
1728 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1730 menu->set_active(true);
1735 /* Show processor automation */
1737 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1738 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1739 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1740 (*ii)->menu_item->set_active (true);
1752 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1754 if (apply_to_selection) {
1755 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1759 /* Hide our automation */
1761 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1762 i->second->set_marked_for_display (false);
1764 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1767 menu->set_active (false);
1771 /* Hide processor automation */
1773 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1774 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1775 (*ii)->menu_item->set_active (false);
1786 RouteTimeAxisView::region_view_added (RegionView* rv)
1788 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1789 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1790 boost::shared_ptr<AutomationTimeAxisView> atv;
1792 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1797 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1798 (*i)->add_ghost(rv);
1802 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1804 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1810 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1812 parent.remove_processor_automation_node (this);
1816 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1819 remove_child (pan->view);
1823 RouteTimeAxisView::ProcessorAutomationNode*
1824 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1826 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1828 if ((*i)->processor == processor) {
1830 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1831 if ((*ii)->what == what) {
1841 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1843 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1846 ProcessorAutomationNode* pan;
1848 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1849 /* session state may never have been saved with new plugin */
1850 error << _("programming error: ")
1851 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1852 processor->name(), what.type(), (int) what.channel(), what.id() )
1862 boost::shared_ptr<AutomationControl> control
1863 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1865 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1866 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1867 _editor, *this, false, parent_canvas,
1868 processor->describe_parameter (what), processor->name()));
1870 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1872 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1875 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1880 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1883 pan->menu_item->set_active (false);
1892 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1894 boost::shared_ptr<Processor> processor (p.lock ());
1896 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1897 /* The Amp processor is a special case and is dealt with separately */
1901 set<Evoral::Parameter> existing;
1903 processor->what_has_data (existing);
1905 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1907 Evoral::Parameter param (*i);
1908 boost::shared_ptr<AutomationLine> al;
1910 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1913 add_processor_automation_curve (processor, param);
1919 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1921 using namespace Menu_Helpers;
1925 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1927 _automation_tracks[param] = track;
1929 /* existing state overrides "show" argument */
1930 string s = track->gui_property ("visible");
1932 show = string_is_affirmative (s);
1935 /* this might or might not change the visibility status, so don't rely on it */
1936 track->set_marked_for_display (show);
1938 if (show && !no_redraw) {
1942 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1943 /* MIDI-related parameters are always in the menu, there's no
1944 reason to rebuild the menu just because we added a automation
1945 lane for one of them. But if we add a non-MIDI automation
1946 lane, then we need to invalidate the display menu.
1948 delete display_menu;
1954 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1956 boost::shared_ptr<Processor> processor (p.lock ());
1958 if (!processor || !processor->display_to_user ()) {
1962 /* we use this override to veto the Amp processor from the plugin menu,
1963 as its automation lane can be accessed using the special "Fader" menu
1967 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1971 using namespace Menu_Helpers;
1972 ProcessorAutomationInfo *rai;
1973 list<ProcessorAutomationInfo*>::iterator x;
1975 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1977 if (automatable.empty()) {
1981 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1982 if ((*x)->processor == processor) {
1987 if (x == processor_automation.end()) {
1989 rai = new ProcessorAutomationInfo (processor);
1990 processor_automation.push_back (rai);
1998 /* any older menu was deleted at the top of processors_changed()
1999 when we cleared the subplugin menu.
2002 rai->menu = manage (new Menu);
2003 MenuList& items = rai->menu->items();
2004 rai->menu->set_name ("ArdourContextMenu");
2008 std::set<Evoral::Parameter> has_visible_automation;
2009 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2011 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2013 ProcessorAutomationNode* pan;
2014 CheckMenuItem* mitem;
2016 string name = processor->describe_parameter (*i);
2018 items.push_back (CheckMenuElem (name));
2019 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2021 _subplugin_menu_map[*i] = mitem;
2023 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2024 mitem->set_active(true);
2027 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2031 pan = new ProcessorAutomationNode (*i, mitem, *this);
2033 rai->lines.push_back (pan);
2037 pan->menu_item = mitem;
2041 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2044 /* add the menu for this processor, because the subplugin
2045 menu is always cleared at the top of processors_changed().
2046 this is the result of some poor design in gtkmm and/or
2050 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2055 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2056 RouteTimeAxisView::ProcessorAutomationNode* pan)
2058 bool showit = pan->menu_item->get_active();
2059 bool redraw = false;
2061 if (pan->view == 0 && showit) {
2062 add_processor_automation_curve (rai->processor, pan->what);
2066 if (pan->view && pan->view->set_marked_for_display (showit)) {
2070 if (redraw && !no_redraw) {
2076 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2078 if (c.type == RouteProcessorChange::MeterPointChange) {
2079 /* nothing to do if only the meter point has changed */
2083 using namespace Menu_Helpers;
2085 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2086 (*i)->valid = false;
2089 setup_processor_menu_and_curves ();
2091 bool deleted_processor_automation = false;
2093 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2095 list<ProcessorAutomationInfo*>::iterator tmp;
2103 processor_automation.erase (i);
2104 deleted_processor_automation = true;
2111 if (deleted_processor_automation && !no_redraw) {
2116 boost::shared_ptr<AutomationLine>
2117 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2119 ProcessorAutomationNode* pan;
2121 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2127 return boost::shared_ptr<AutomationLine>();
2131 RouteTimeAxisView::reset_processor_automation_curves ()
2133 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2139 RouteTimeAxisView::update_rec_display ()
2141 RouteUI::update_rec_display ();
2142 name_entry.set_sensitive (!_route->record_enabled());
2146 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2148 if (apply_to_selection) {
2149 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2153 _view->set_layer_display (d);
2156 set_gui_property (X_("layer-display"), enum_2_string (d));
2161 RouteTimeAxisView::layer_display () const
2164 return _view->layer_display ();
2167 /* we don't know, since we don't have a _view, so just return something */
2173 boost::shared_ptr<AutomationTimeAxisView>
2174 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2176 AutomationTracks::iterator i = _automation_tracks.find(param);
2177 if (i != _automation_tracks.end()) {
2180 return boost::shared_ptr<AutomationTimeAxisView>();
2185 RouteTimeAxisView::fast_update ()
2187 gm.get_level_meter().update_meters ();
2191 RouteTimeAxisView::hide_meter ()
2194 gm.get_level_meter().hide_meters ();
2198 RouteTimeAxisView::show_meter ()
2204 RouteTimeAxisView::reset_meter ()
2206 if (Config->get_show_track_meters()) {
2207 gm.get_level_meter().setup_meters (height-5);
2214 RouteTimeAxisView::clear_meter ()
2216 gm.get_level_meter().clear_meters ();
2220 RouteTimeAxisView::meter_changed ()
2222 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2227 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2233 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2235 using namespace Menu_Helpers;
2237 if (!_underlay_streams.empty()) {
2238 MenuList& parent_items = parent_menu->items();
2239 Menu* gs_menu = manage (new Menu);
2240 gs_menu->set_name ("ArdourContextMenu");
2241 MenuList& gs_items = gs_menu->items();
2243 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2245 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2246 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2247 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2253 RouteTimeAxisView::set_underlay_state()
2255 if (!underlay_xml_node) {
2259 XMLNodeList nlist = underlay_xml_node->children();
2260 XMLNodeConstIterator niter;
2261 XMLNode *child_node;
2263 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2264 child_node = *niter;
2266 if (child_node->name() != "Underlay") {
2270 XMLProperty* prop = child_node->property ("id");
2272 PBD::ID id (prop->value());
2274 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2277 add_underlay(v->view(), false);
2286 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2292 RouteTimeAxisView& other = v->trackview();
2294 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2295 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2296 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2300 _underlay_streams.push_back(v);
2301 other._underlay_mirrors.push_back(this);
2303 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2305 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2307 if (!underlay_xml_node) {
2308 underlay_xml_node = xml_node->add_child("Underlays");
2311 XMLNode* node = underlay_xml_node->add_child("Underlay");
2312 XMLProperty* prop = node->add_property("id");
2313 prop->set_value(v->trackview().route()->id().to_s());
2320 RouteTimeAxisView::remove_underlay (StreamView* v)
2326 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2327 RouteTimeAxisView& other = v->trackview();
2329 if (it != _underlay_streams.end()) {
2330 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2332 if (gm == other._underlay_mirrors.end()) {
2333 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2337 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2339 _underlay_streams.erase(it);
2340 other._underlay_mirrors.erase(gm);
2342 if (underlay_xml_node) {
2343 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2349 RouteTimeAxisView::set_button_names ()
2351 if (_route && _route->solo_safe()) {
2352 solo_button->remove ();
2353 if (solo_safe_pixbuf == 0) {
2354 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2356 solo_button->set_image (solo_safe_pixbuf);
2357 solo_button->set_text (string());
2359 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2360 if (Config->get_solo_control_is_listen_control()) {
2361 switch (Config->get_listen_position()) {
2362 case AfterFaderListen:
2363 solo_button->set_text (_("A"));
2365 case PreFaderListen:
2366 solo_button->set_text (_("P"));
2370 solo_button->set_text (_("s"));
2373 mute_button->set_text (_("m"));
2377 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2379 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2380 if (i != _main_automation_menu_map.end()) {
2384 i = _subplugin_menu_map.find (param);
2385 if (i != _subplugin_menu_map.end()) {
2393 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2395 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2397 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2401 gain_track.reset (new AutomationTimeAxisView (_session,
2402 _route, _route->amp(), c, param,
2407 _route->amp()->describe_parameter(param)));
2410 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2413 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2417 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2419 l->push_back (rv->region());
2423 RouteTimeAxisView::combine_regions ()
2425 /* as of may 2011, we do not offer uncombine for MIDI tracks
2428 if (!is_audio_track()) {
2436 Playlist::RegionList selected_regions;
2437 boost::shared_ptr<Playlist> playlist = track()->playlist();
2439 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2441 if (selected_regions.size() < 2) {
2445 playlist->clear_changes ();
2446 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2448 _session->add_command (new StatefulDiffCommand (playlist));
2449 /* make the new region be selected */
2451 return _view->find_view (compound_region);
2455 RouteTimeAxisView::uncombine_regions ()
2457 /* as of may 2011, we do not offer uncombine for MIDI tracks
2459 if (!is_audio_track()) {
2467 Playlist::RegionList selected_regions;
2468 boost::shared_ptr<Playlist> playlist = track()->playlist();
2470 /* have to grab selected regions first because the uncombine is going
2471 * to change that in the middle of the list traverse
2474 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2476 playlist->clear_changes ();
2478 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2479 playlist->uncombine (*i);
2482 _session->add_command (new StatefulDiffCommand (playlist));
2486 RouteTimeAxisView::state_id() const
2488 return string_compose ("rtav %1", _route->id().to_s());