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"
66 #include "global_signals.h"
67 #include "route_time_axis.h"
68 #include "automation_time_axis.h"
69 #include "canvas_impl.h"
70 #include "crossfade_view.h"
72 #include "gui_thread.h"
74 #include "playlist_selector.h"
75 #include "point_selection.h"
77 #include "public_editor.h"
78 #include "region_view.h"
79 #include "rgb_macros.h"
80 #include "selection.h"
81 #include "simplerect.h"
82 #include "streamview.h"
84 #include "route_group_menu.h"
86 #include "ardour/track.h"
90 using namespace ARDOUR;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
97 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
100 RouteTimeAxisView::setup_slider_pix ()
102 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
103 throw failed_constructor ();
107 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
110 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
112 , parent_canvas (canvas)
113 , button_table (3, 3)
114 , route_group_button (_("g"))
115 , playlist_button (_("p"))
116 , automation_button (_("a"))
117 , automation_action_menu (0)
118 , plugins_submenu_item (0)
119 , route_group_menu (0)
120 , playlist_action_menu (0)
122 , color_mode_menu (0)
123 , gm (sess, slider, true, 115)
128 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
130 RouteUI::set_route (rt);
132 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133 gm.get_level_meter().set_no_show_all();
134 gm.get_level_meter().setup_meters(50);
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_hidden()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("TrackGroupButton");
160 playlist_button.set_name ("TrackPlaylistButton");
161 automation_button.set_name ("TrackAutomationButton");
163 route_group_button.unset_flags (Gtk::CAN_FOCUS);
164 playlist_button.unset_flags (Gtk::CAN_FOCUS);
165 automation_button.unset_flags (Gtk::CAN_FOCUS);
167 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
168 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
169 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
175 rec_enable_button->remove ();
177 switch (track()->mode()) {
179 case ARDOUR::NonLayered:
180 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
182 case ARDOUR::Destructive:
183 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
186 rec_enable_button->show_all ();
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->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
757 case ARDOUR::Destructive:
758 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
762 rec_enable_button->show_all ();
767 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
773 TimeAxisView::show_timestretch (start, end);
783 /* check that the time selection was made in our route, or our route group.
784 remember that route_group() == 0 implies the route is *not* in a edit group.
787 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
788 /* this doesn't apply to us */
792 /* ignore it if our edit group is not active */
794 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
799 if (timestretch_rect == 0) {
800 timestretch_rect = new SimpleRect (*canvas_display ());
801 timestretch_rect->property_x1() = 0.0;
802 timestretch_rect->property_y1() = 0.0;
803 timestretch_rect->property_x2() = 0.0;
804 timestretch_rect->property_y2() = 0.0;
805 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
806 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
809 timestretch_rect->show ();
810 timestretch_rect->raise_to_top ();
812 x1 = start / _editor.get_current_zoom();
813 x2 = (end - 1) / _editor.get_current_zoom();
814 y2 = current_height() - 2;
816 timestretch_rect->property_x1() = x1;
817 timestretch_rect->property_y1() = 1.0;
818 timestretch_rect->property_x2() = x2;
819 timestretch_rect->property_y2() = y2;
823 RouteTimeAxisView::hide_timestretch ()
825 TimeAxisView::hide_timestretch ();
827 if (timestretch_rect) {
828 timestretch_rect->hide ();
833 RouteTimeAxisView::show_selection (TimeSelection& ts)
837 /* ignore it if our edit group is not active or if the selection was started
838 in some other track or route group (remember that route_group() == 0 means
839 that the track is not in an route group).
842 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
843 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
849 TimeAxisView::show_selection (ts);
853 RouteTimeAxisView::set_height (uint32_t h)
856 bool height_changed = (height == 0) || (h != height);
857 gm.get_level_meter().setup_meters (gmlen);
859 TimeAxisView::set_height (h);
862 _view->set_height ((double) current_height());
865 if (height >= preset_height (HeightNormal)) {
869 gm.get_gain_slider().show();
871 if (!_route || _route->is_monitor()) {
876 if (rec_enable_button)
877 rec_enable_button->show();
879 route_group_button.show();
880 automation_button.show();
882 if (is_track() && track()->mode() == ARDOUR::Normal) {
883 playlist_button.show();
890 gm.get_gain_slider().hide();
892 if (!_route || _route->is_monitor()) {
897 if (rec_enable_button)
898 rec_enable_button->show();
900 route_group_button.hide ();
901 automation_button.hide ();
903 if (is_track() && track()->mode() == ARDOUR::Normal) {
904 playlist_button.hide ();
909 if (height_changed && !no_redraw) {
910 /* only emit the signal if the height really changed */
916 RouteTimeAxisView::route_color_changed ()
919 _view->apply_color (color(), StreamView::RegionColor);
924 RouteTimeAxisView::reset_samples_per_unit ()
926 set_samples_per_unit (_editor.get_current_zoom());
930 RouteTimeAxisView::horizontal_position_changed ()
933 _view->horizontal_position_changed ();
938 RouteTimeAxisView::set_samples_per_unit (double spu)
943 speed = track()->speed();
947 _view->set_samples_per_unit (spu * speed);
950 TimeAxisView::set_samples_per_unit (spu * speed);
954 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
956 if (!mitem->get_active()) {
957 /* this is one of the two calls made when these radio menu items change status. this one
958 is for the item that became inactive, and we want to ignore it.
963 if (apply_to_selection) {
964 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
967 track()->set_align_choice (choice);
973 RouteTimeAxisView::rename_current_playlist ()
975 ArdourPrompter prompter (true);
978 boost::shared_ptr<Track> tr = track();
979 if (!tr || tr->destructive()) {
983 boost::shared_ptr<Playlist> pl = tr->playlist();
988 prompter.set_title (_("Rename Playlist"));
989 prompter.set_prompt (_("New name for playlist:"));
990 prompter.set_initial_text (pl->name());
991 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
992 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
994 switch (prompter.run ()) {
995 case Gtk::RESPONSE_ACCEPT:
996 prompter.get_result (name);
1008 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1010 std::string ret (basename);
1012 std::string const group_string = "." + route_group()->name() + ".";
1014 // iterate through all playlists
1016 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1017 std::string tmp = (*i)->name();
1019 std::string::size_type idx = tmp.find(group_string);
1020 // find those which belong to this group
1021 if (idx != string::npos) {
1022 tmp = tmp.substr(idx + group_string.length());
1024 // and find the largest current number
1025 int x = atoi(tmp.c_str());
1026 if (x > maxnumber) {
1035 snprintf (buf, sizeof(buf), "%d", maxnumber);
1037 ret = this->name() + "." + route_group()->name () + "." + buf;
1043 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1047 boost::shared_ptr<Track> tr = track ();
1048 if (!tr || tr->destructive()) {
1052 boost::shared_ptr<const Playlist> pl = tr->playlist();
1059 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1060 name = resolve_new_group_playlist_name(name, playlists_before_op);
1063 while (_session->playlists->by_name(name)) {
1064 name = Playlist::bump_name (name, *_session);
1067 // TODO: The prompter "new" button should be de-activated if the user
1068 // specifies a playlist name which already exists in the session.
1072 ArdourPrompter prompter (true);
1074 prompter.set_title (_("New Copy Playlist"));
1075 prompter.set_prompt (_("Name for new playlist:"));
1076 prompter.set_initial_text (name);
1077 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1078 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1079 prompter.show_all ();
1081 switch (prompter.run ()) {
1082 case Gtk::RESPONSE_ACCEPT:
1083 prompter.get_result (name);
1091 if (name.length()) {
1092 tr->use_copy_playlist ();
1093 tr->playlist()->set_name (name);
1098 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1102 boost::shared_ptr<Track> tr = track ();
1103 if (!tr || tr->destructive()) {
1107 boost::shared_ptr<const Playlist> pl = tr->playlist();
1114 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1115 name = resolve_new_group_playlist_name(name,playlists_before_op);
1118 while (_session->playlists->by_name(name)) {
1119 name = Playlist::bump_name (name, *_session);
1125 ArdourPrompter prompter (true);
1127 prompter.set_title (_("New Playlist"));
1128 prompter.set_prompt (_("Name for new playlist:"));
1129 prompter.set_initial_text (name);
1130 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1131 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1133 switch (prompter.run ()) {
1134 case Gtk::RESPONSE_ACCEPT:
1135 prompter.get_result (name);
1143 if (name.length()) {
1144 tr->use_new_playlist ();
1145 tr->playlist()->set_name (name);
1150 RouteTimeAxisView::clear_playlist ()
1152 boost::shared_ptr<Track> tr = track ();
1153 if (!tr || tr->destructive()) {
1157 boost::shared_ptr<Playlist> pl = tr->playlist();
1162 _editor.clear_playlist (pl);
1166 RouteTimeAxisView::speed_changed ()
1168 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1172 RouteTimeAxisView::update_diskstream_display ()
1182 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1184 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1186 /* special case: select/deselect all tracks */
1187 if (_editor.get_selection().selected (this)) {
1188 _editor.get_selection().clear_tracks ();
1190 _editor.select_all_tracks ();
1196 switch (ArdourKeyboard::selection_type (ev->state)) {
1197 case Selection::Toggle:
1198 _editor.get_selection().toggle (this);
1201 case Selection::Set:
1202 _editor.get_selection().set (this);
1205 case Selection::Extend:
1206 _editor.extend_selection_to_track (*this);
1209 case Selection::Add:
1210 _editor.get_selection().add (this);
1216 RouteTimeAxisView::set_selected_points (PointSelection& points)
1218 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1219 (*i)->set_selected_points (points);
1224 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1227 _view->set_selected_regionviews (regions);
1231 /** Add the selectable things that we have to a list.
1232 * @param results List to add things to.
1235 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1240 speed = track()->speed();
1243 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1244 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1246 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1247 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1250 /* pick up visible automation tracks */
1252 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1253 if (!(*i)->hidden()) {
1254 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1260 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1263 _view->get_inverted_selectables (sel, results);
1266 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1267 if (!(*i)->hidden()) {
1268 (*i)->get_inverted_selectables (sel, results);
1276 RouteTimeAxisView::route_group () const
1278 return _route->route_group();
1282 RouteTimeAxisView::name() const
1284 return _route->name();
1287 boost::shared_ptr<Playlist>
1288 RouteTimeAxisView::playlist () const
1290 boost::shared_ptr<Track> tr;
1292 if ((tr = track()) != 0) {
1293 return tr->playlist();
1295 return boost::shared_ptr<Playlist> ();
1300 RouteTimeAxisView::name_entry_changed ()
1302 string x = name_entry.get_text ();
1304 if (x == _route->name()) {
1308 strip_whitespace_edges (x);
1310 if (x.length() == 0) {
1311 name_entry.set_text (_route->name());
1315 if (_session->route_name_internal (x)) {
1316 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1318 name_entry.grab_focus ();
1319 } else if (RouteUI::verify_new_route_name (x)) {
1320 _route->set_name (x);
1322 name_entry.grab_focus ();
1326 boost::shared_ptr<Region>
1327 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1329 boost::shared_ptr<Playlist> pl = playlist ();
1332 return pl->find_next_region (pos, point, dir);
1335 return boost::shared_ptr<Region> ();
1339 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1341 boost::shared_ptr<Playlist> pl = playlist ();
1344 return pl->find_next_region_boundary (pos, dir);
1351 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1353 boost::shared_ptr<Playlist> what_we_got;
1354 boost::shared_ptr<Track> tr = track ();
1355 boost::shared_ptr<Playlist> playlist;
1358 /* route is a bus, not a track */
1362 playlist = tr->playlist();
1364 TimeSelection time (selection.time);
1365 float const speed = tr->speed();
1366 if (speed != 1.0f) {
1367 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1368 (*i).start = session_frame_to_track_frame((*i).start, speed);
1369 (*i).end = session_frame_to_track_frame((*i).end, speed);
1373 playlist->clear_changes ();
1374 playlist->clear_owned_changes ();
1378 if (playlist->cut (time) != 0) {
1379 vector<Command*> cmds;
1380 playlist->rdiff (cmds);
1381 _session->add_commands (cmds);
1383 _session->add_command (new StatefulDiffCommand (playlist));
1388 if ((what_we_got = playlist->cut (time)) != 0) {
1389 _editor.get_cut_buffer().add (what_we_got);
1390 vector<Command*> cmds;
1391 playlist->rdiff (cmds);
1392 _session->add_commands (cmds);
1394 _session->add_command (new StatefulDiffCommand (playlist));
1398 if ((what_we_got = playlist->copy (time)) != 0) {
1399 _editor.get_cut_buffer().add (what_we_got);
1404 if ((what_we_got = playlist->cut (time)) != 0) {
1406 vector<Command*> cmds;
1407 playlist->rdiff (cmds);
1408 _session->add_commands (cmds);
1409 _session->add_command (new StatefulDiffCommand (playlist));
1410 what_we_got->release ();
1417 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1423 boost::shared_ptr<Playlist> pl = playlist ();
1424 PlaylistSelection::iterator p;
1426 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1428 if (p == selection.playlists.end()) {
1432 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1434 if (track()->speed() != 1.0f) {
1435 pos = session_frame_to_track_frame (pos, track()->speed());
1436 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1439 pl->clear_changes ();
1440 pl->paste (*p, pos, times);
1441 _session->add_command (new StatefulDiffCommand (pl));
1447 struct PlaylistSorter {
1448 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1449 return a->sort_id() < b->sort_id();
1454 RouteTimeAxisView::build_playlist_menu ()
1456 using namespace Menu_Helpers;
1462 delete playlist_action_menu;
1463 playlist_action_menu = new Menu;
1464 playlist_action_menu->set_name ("ArdourContextMenu");
1466 MenuList& playlist_items = playlist_action_menu->items();
1467 playlist_action_menu->set_name ("ArdourContextMenu");
1468 playlist_items.clear();
1470 RadioMenuItem::Group playlist_group;
1471 boost::shared_ptr<Track> tr = track ();
1473 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1475 /* sort the playlists */
1477 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1479 /* add the playlists to the menu */
1480 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1481 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1482 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1483 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1485 if (tr->playlist()->id() == (*i)->id()) {
1491 playlist_items.push_back (SeparatorElem());
1492 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1493 playlist_items.push_back (SeparatorElem());
1495 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1496 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1497 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1500 // Use a label which tells the user what is happening
1501 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1502 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1506 playlist_items.push_back (SeparatorElem());
1507 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1508 playlist_items.push_back (SeparatorElem());
1510 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1514 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1516 assert (is_track());
1518 // exit if we were triggered by deactivating the old playlist
1519 if (!item->get_active()) {
1523 boost::shared_ptr<Playlist> pl (wpl.lock());
1529 if (track()->playlist() == pl) {
1530 // exit when use_playlist is called by the creation of the playlist menu
1531 // or the playlist choice is unchanged
1535 track()->use_playlist (pl);
1537 RouteGroup* rg = route_group();
1539 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1540 std::string group_string = "." + rg->name() + ".";
1542 std::string take_name = pl->name();
1543 std::string::size_type idx = take_name.find(group_string);
1545 if (idx == std::string::npos)
1548 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1550 boost::shared_ptr<RouteList> rl (rg->route_list());
1552 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1553 if ( (*i) == this->route()) {
1557 std::string playlist_name = (*i)->name()+group_string+take_name;
1559 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1564 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1566 // No playlist for this track for this take yet, make it
1567 track->use_new_playlist();
1568 track->playlist()->set_name(playlist_name);
1570 track->use_playlist(ipl);
1577 RouteTimeAxisView::show_playlist_selector ()
1579 _editor.playlist_selector().show_for (this);
1583 RouteTimeAxisView::map_frozen ()
1589 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1591 switch (track()->freeze_state()) {
1593 playlist_button.set_sensitive (false);
1594 rec_enable_button->set_sensitive (false);
1597 playlist_button.set_sensitive (true);
1598 rec_enable_button->set_sensitive (true);
1604 RouteTimeAxisView::color_handler ()
1606 //case cTimeStretchOutline:
1607 if (timestretch_rect) {
1608 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1610 //case cTimeStretchFill:
1611 if (timestretch_rect) {
1612 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1618 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1619 * Will add track if necessary.
1622 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1624 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1625 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1628 /* it doesn't exist yet, so we don't care about the button state: just add it */
1629 create_automation_child (param, true);
1632 bool yn = menu->get_active();
1633 bool changed = false;
1635 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1637 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1638 will have done that for us.
1641 if (changed && !no_redraw) {
1649 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1651 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1657 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1659 if (menu && !_hidden) {
1660 ignore_toggle = true;
1661 menu->set_active (false);
1662 ignore_toggle = false;
1665 if (_route && !no_redraw) {
1672 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1674 if (apply_to_selection) {
1675 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1679 /* Show our automation */
1681 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1682 i->second->set_marked_for_display (true);
1684 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1687 menu->set_active(true);
1692 /* Show processor automation */
1694 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1695 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1696 if ((*ii)->view == 0) {
1697 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1700 (*ii)->menu_item->set_active (true);
1713 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1715 if (apply_to_selection) {
1716 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1720 /* Show our automation */
1722 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1723 if (i->second->has_automation()) {
1724 i->second->set_marked_for_display (true);
1726 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1728 menu->set_active(true);
1733 /* Show processor automation */
1735 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1736 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1737 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1738 (*ii)->menu_item->set_active (true);
1750 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1752 if (apply_to_selection) {
1753 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1757 /* Hide our automation */
1759 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1760 i->second->set_marked_for_display (false);
1762 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1765 menu->set_active (false);
1769 /* Hide processor automation */
1771 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1772 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1773 (*ii)->menu_item->set_active (false);
1784 RouteTimeAxisView::region_view_added (RegionView* rv)
1786 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1787 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1788 boost::shared_ptr<AutomationTimeAxisView> atv;
1790 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1795 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1796 (*i)->add_ghost(rv);
1800 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1802 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1808 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1810 parent.remove_processor_automation_node (this);
1814 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1817 remove_child (pan->view);
1821 RouteTimeAxisView::ProcessorAutomationNode*
1822 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1824 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1826 if ((*i)->processor == processor) {
1828 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1829 if ((*ii)->what == what) {
1839 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1841 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1844 ProcessorAutomationNode* pan;
1846 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1847 /* session state may never have been saved with new plugin */
1848 error << _("programming error: ")
1849 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1850 processor->name(), what.type(), (int) what.channel(), what.id() )
1860 boost::shared_ptr<AutomationControl> control
1861 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1863 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1864 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1865 _editor, *this, false, parent_canvas,
1866 processor->describe_parameter (what), processor->name()));
1868 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1870 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1873 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1878 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1881 pan->menu_item->set_active (false);
1890 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1892 boost::shared_ptr<Processor> processor (p.lock ());
1894 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1895 /* The Amp processor is a special case and is dealt with separately */
1899 set<Evoral::Parameter> existing;
1901 processor->what_has_data (existing);
1903 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1905 Evoral::Parameter param (*i);
1906 boost::shared_ptr<AutomationLine> al;
1908 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1911 add_processor_automation_curve (processor, param);
1917 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1919 using namespace Menu_Helpers;
1923 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1925 _automation_tracks[param] = track;
1927 /* existing state overrides "show" argument */
1928 string s = track->gui_property ("visible");
1930 show = string_is_affirmative (s);
1933 /* this might or might not change the visibility status, so don't rely on it */
1934 track->set_marked_for_display (show);
1936 if (show && !no_redraw) {
1940 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1941 /* MIDI-related parameters are always in the menu, there's no
1942 reason to rebuild the menu just because we added a automation
1943 lane for one of them. But if we add a non-MIDI automation
1944 lane, then we need to invalidate the display menu.
1946 delete display_menu;
1952 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1954 boost::shared_ptr<Processor> processor (p.lock ());
1956 if (!processor || !processor->display_to_user ()) {
1960 /* we use this override to veto the Amp processor from the plugin menu,
1961 as its automation lane can be accessed using the special "Fader" menu
1965 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1969 using namespace Menu_Helpers;
1970 ProcessorAutomationInfo *rai;
1971 list<ProcessorAutomationInfo*>::iterator x;
1973 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1975 if (automatable.empty()) {
1979 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1980 if ((*x)->processor == processor) {
1985 if (x == processor_automation.end()) {
1987 rai = new ProcessorAutomationInfo (processor);
1988 processor_automation.push_back (rai);
1996 /* any older menu was deleted at the top of processors_changed()
1997 when we cleared the subplugin menu.
2000 rai->menu = manage (new Menu);
2001 MenuList& items = rai->menu->items();
2002 rai->menu->set_name ("ArdourContextMenu");
2006 std::set<Evoral::Parameter> has_visible_automation;
2007 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2009 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2011 ProcessorAutomationNode* pan;
2012 CheckMenuItem* mitem;
2014 string name = processor->describe_parameter (*i);
2016 items.push_back (CheckMenuElem (name));
2017 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2019 _subplugin_menu_map[*i] = mitem;
2021 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2022 mitem->set_active(true);
2025 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2029 pan = new ProcessorAutomationNode (*i, mitem, *this);
2031 rai->lines.push_back (pan);
2035 pan->menu_item = mitem;
2039 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2042 /* add the menu for this processor, because the subplugin
2043 menu is always cleared at the top of processors_changed().
2044 this is the result of some poor design in gtkmm and/or
2048 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2053 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2054 RouteTimeAxisView::ProcessorAutomationNode* pan)
2056 bool showit = pan->menu_item->get_active();
2057 bool redraw = false;
2059 if (pan->view == 0 && showit) {
2060 add_processor_automation_curve (rai->processor, pan->what);
2064 if (pan->view && pan->view->set_marked_for_display (showit)) {
2068 if (redraw && !no_redraw) {
2074 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2076 if (c.type == RouteProcessorChange::MeterPointChange) {
2077 /* nothing to do if only the meter point has changed */
2081 using namespace Menu_Helpers;
2083 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2084 (*i)->valid = false;
2087 setup_processor_menu_and_curves ();
2089 bool deleted_processor_automation = false;
2091 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2093 list<ProcessorAutomationInfo*>::iterator tmp;
2101 processor_automation.erase (i);
2102 deleted_processor_automation = true;
2109 if (deleted_processor_automation && !no_redraw) {
2114 boost::shared_ptr<AutomationLine>
2115 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2117 ProcessorAutomationNode* pan;
2119 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2125 return boost::shared_ptr<AutomationLine>();
2129 RouteTimeAxisView::reset_processor_automation_curves ()
2131 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2137 RouteTimeAxisView::update_rec_display ()
2139 RouteUI::update_rec_display ();
2140 name_entry.set_sensitive (!_route->record_enabled());
2144 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2146 if (apply_to_selection) {
2147 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2151 _view->set_layer_display (d);
2154 set_gui_property (X_("layer-display"), enum_2_string (d));
2159 RouteTimeAxisView::layer_display () const
2162 return _view->layer_display ();
2165 /* we don't know, since we don't have a _view, so just return something */
2171 boost::shared_ptr<AutomationTimeAxisView>
2172 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2174 AutomationTracks::iterator i = _automation_tracks.find(param);
2175 if (i != _automation_tracks.end()) {
2178 return boost::shared_ptr<AutomationTimeAxisView>();
2183 RouteTimeAxisView::fast_update ()
2185 gm.get_level_meter().update_meters ();
2189 RouteTimeAxisView::hide_meter ()
2192 gm.get_level_meter().hide_meters ();
2196 RouteTimeAxisView::show_meter ()
2202 RouteTimeAxisView::reset_meter ()
2204 if (Config->get_show_track_meters()) {
2205 gm.get_level_meter().setup_meters (height-5);
2212 RouteTimeAxisView::clear_meter ()
2214 gm.get_level_meter().clear_meters ();
2218 RouteTimeAxisView::meter_changed ()
2220 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2225 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2231 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2233 using namespace Menu_Helpers;
2235 if (!_underlay_streams.empty()) {
2236 MenuList& parent_items = parent_menu->items();
2237 Menu* gs_menu = manage (new Menu);
2238 gs_menu->set_name ("ArdourContextMenu");
2239 MenuList& gs_items = gs_menu->items();
2241 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2243 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2244 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2245 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2251 RouteTimeAxisView::set_underlay_state()
2253 if (!underlay_xml_node) {
2257 XMLNodeList nlist = underlay_xml_node->children();
2258 XMLNodeConstIterator niter;
2259 XMLNode *child_node;
2261 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2262 child_node = *niter;
2264 if (child_node->name() != "Underlay") {
2268 XMLProperty* prop = child_node->property ("id");
2270 PBD::ID id (prop->value());
2272 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2275 add_underlay(v->view(), false);
2284 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2290 RouteTimeAxisView& other = v->trackview();
2292 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2293 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2294 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2298 _underlay_streams.push_back(v);
2299 other._underlay_mirrors.push_back(this);
2301 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2303 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2305 if (!underlay_xml_node) {
2306 underlay_xml_node = xml_node->add_child("Underlays");
2309 XMLNode* node = underlay_xml_node->add_child("Underlay");
2310 XMLProperty* prop = node->add_property("id");
2311 prop->set_value(v->trackview().route()->id().to_s());
2318 RouteTimeAxisView::remove_underlay (StreamView* v)
2324 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2325 RouteTimeAxisView& other = v->trackview();
2327 if (it != _underlay_streams.end()) {
2328 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2330 if (gm == other._underlay_mirrors.end()) {
2331 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2335 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2337 _underlay_streams.erase(it);
2338 other._underlay_mirrors.erase(gm);
2340 if (underlay_xml_node) {
2341 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2347 RouteTimeAxisView::set_button_names ()
2349 rec_enable_button_label.set_text (_("r"));
2351 if (_route && _route->solo_safe()) {
2352 solo_button_label.set_text (X_("!"));
2354 if (Config->get_solo_control_is_listen_control()) {
2355 switch (Config->get_listen_position()) {
2356 case AfterFaderListen:
2357 solo_button_label.set_text (_("A"));
2359 case PreFaderListen:
2360 solo_button_label.set_text (_("P"));
2364 solo_button_label.set_text (_("s"));
2367 mute_button_label.set_text (_("m"));
2371 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2373 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2374 if (i != _main_automation_menu_map.end()) {
2378 i = _subplugin_menu_map.find (param);
2379 if (i != _subplugin_menu_map.end()) {
2387 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2389 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2391 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2395 gain_track.reset (new AutomationTimeAxisView (_session,
2396 _route, _route->amp(), c, param,
2401 _route->amp()->describe_parameter(param)));
2404 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2407 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2411 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2413 l->push_back (rv->region());
2417 RouteTimeAxisView::combine_regions ()
2419 /* as of may 2011, we do not offer uncombine for MIDI tracks
2422 if (!is_audio_track()) {
2430 Playlist::RegionList selected_regions;
2431 boost::shared_ptr<Playlist> playlist = track()->playlist();
2433 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2435 if (selected_regions.size() < 2) {
2439 playlist->clear_changes ();
2440 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2442 _session->add_command (new StatefulDiffCommand (playlist));
2443 /* make the new region be selected */
2445 return _view->find_view (compound_region);
2449 RouteTimeAxisView::uncombine_regions ()
2451 /* as of may 2011, we do not offer uncombine for MIDI tracks
2453 if (!is_audio_track()) {
2461 Playlist::RegionList selected_regions;
2462 boost::shared_ptr<Playlist> playlist = track()->playlist();
2464 /* have to grab selected regions first because the uncombine is going
2465 * to change that in the middle of the list traverse
2468 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2470 playlist->clear_changes ();
2472 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2473 playlist->uncombine (*i);
2476 _session->add_command (new StatefulDiffCommand (playlist));
2480 RouteTimeAxisView::state_id() const
2482 return string_compose ("rtav %1", _route->id().to_s());