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;
98 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
101 RouteTimeAxisView::setup_slider_pix ()
103 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
104 throw failed_constructor ();
108 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
111 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
113 , parent_canvas (canvas)
115 , button_table (3, 3)
116 , route_group_button (_("g"))
117 , playlist_button (_("p"))
118 , automation_button (_("a"))
119 , automation_action_menu (0)
120 , plugins_submenu_item (0)
121 , route_group_menu (0)
122 , playlist_action_menu (0)
124 , color_mode_menu (0)
125 , gm (sess, slider, true, 115)
130 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
132 RouteUI::set_route (rt);
134 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
135 gm.get_level_meter().set_no_show_all();
136 gm.get_level_meter().setup_meters(50);
138 string str = gui_property ("height");
140 set_height (atoi (str));
142 set_height (preset_height (HeightNormal));
145 if (!_route->is_hidden()) {
146 if (gui_property ("visible").empty()) {
147 set_gui_property ("visible", true);
150 set_gui_property ("visible", false);
154 update_solo_display ();
156 timestretch_rect = 0;
159 ignore_toggle = false;
161 route_group_button.set_name ("TrackGroupButton");
162 playlist_button.set_name ("TrackPlaylistButton");
163 automation_button.set_name ("TrackAutomationButton");
165 route_group_button.unset_flags (Gtk::CAN_FOCUS);
166 playlist_button.unset_flags (Gtk::CAN_FOCUS);
167 automation_button.unset_flags (Gtk::CAN_FOCUS);
169 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
170 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
171 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
177 rec_enable_button->remove ();
179 switch (track()->mode()) {
181 case ARDOUR::NonLayered:
182 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
184 case ARDOUR::Destructive:
185 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
188 rec_enable_button->show_all ();
190 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 if (is_midi_track()) {
193 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
195 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
198 rec_enable_button->set_sensitive (_session->writable());
201 controls_hbox.pack_start(gm.get_level_meter(), false, false);
202 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
203 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
206 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
208 if (!_route->is_master()) {
209 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
212 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
215 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
216 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
217 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
218 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
219 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
223 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
225 if (is_track() && track()->mode() == ARDOUR::Normal) {
226 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
231 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
232 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
236 str = gui_property ("layer-display");
238 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
241 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
242 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
244 /* pick up the correct freeze state */
248 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
249 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
250 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
252 PropertyList* plist = new PropertyList();
254 plist->add (ARDOUR::Properties::edit, true);
255 plist->add (ARDOUR::Properties::mute, true);
256 plist->add (ARDOUR::Properties::solo, true);
258 route_group_menu = new RouteGroupMenu (_session, plist);
260 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
261 gm.get_gain_slider().set_name ("TrackGainFader");
263 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
269 RouteTimeAxisView::~RouteTimeAxisView ()
271 CatchDeletion (this);
273 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
277 delete playlist_action_menu;
278 playlist_action_menu = 0;
283 _automation_tracks.clear ();
285 delete route_group_menu;
289 RouteTimeAxisView::post_construct ()
291 /* map current state of the route */
293 update_diskstream_display ();
294 setup_processor_menu_and_curves ();
295 reset_processor_automation_curves ();
298 /** Set up the processor menu for the current set of processors, and
299 * display automation curves for any parameters which have data.
302 RouteTimeAxisView::setup_processor_menu_and_curves ()
304 _subplugin_menu_map.clear ();
305 subplugin_menu.items().clear ();
306 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
307 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
311 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
313 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
314 if (_route->route_group()) {
315 _route->route_group()->remove (_route);
321 r.push_back (route ());
323 route_group_menu->build (r);
324 route_group_menu->menu()->popup (ev->button, ev->time);
330 RouteTimeAxisView::playlist_changed ()
336 RouteTimeAxisView::label_view ()
338 string x = _route->name();
340 if (x != name_entry.get_text()) {
341 name_entry.set_text (x);
344 if (x != name_label.get_text()) {
345 name_label.set_text (x);
348 ARDOUR_UI::instance()->set_tip (name_entry, x);
352 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
354 if (what_changed.contains (ARDOUR::Properties::name)) {
360 RouteTimeAxisView::take_name_changed (void *src)
368 RouteTimeAxisView::playlist_click ()
370 build_playlist_menu ();
371 conditionally_add_to_selection ();
372 playlist_action_menu->popup (1, gtk_get_current_event_time());
376 RouteTimeAxisView::automation_click ()
378 conditionally_add_to_selection ();
379 build_automation_action_menu (false);
380 automation_action_menu->popup (1, gtk_get_current_event_time());
384 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
386 using namespace Menu_Helpers;
388 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
389 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
392 detach_menu (subplugin_menu);
394 _main_automation_menu_map.clear ();
395 delete automation_action_menu;
396 automation_action_menu = new Menu;
398 MenuList& items = automation_action_menu->items();
400 automation_action_menu->set_name ("ArdourContextMenu");
402 items.push_back (MenuElem (_("Show All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
405 items.push_back (MenuElem (_("Show Existing Automation"),
406 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
408 items.push_back (MenuElem (_("Hide All Automation"),
409 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
411 items.push_back (SeparatorElem ());
413 /* Attach the plugin submenu. It may have previously been used elsewhere,
414 so it was detached above */
416 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
417 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
421 RouteTimeAxisView::build_display_menu ()
423 using namespace Menu_Helpers;
427 TimeAxisView::build_display_menu ();
429 /* now fill it with our stuff */
431 MenuList& items = display_menu->items();
432 display_menu->set_name ("ArdourContextMenu");
434 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
437 detach_menu (*_size_menu);
440 items.push_back (MenuElem (_("Height"), *_size_menu));
442 items.push_back (SeparatorElem());
444 if (!Profile->get_sae()) {
445 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
446 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
447 items.push_back (SeparatorElem());
450 // Hook for derived classes to add type specific stuff
451 append_extra_display_menu_items ();
455 Menu* layers_menu = manage (new Menu);
456 MenuList &layers_items = layers_menu->items();
457 layers_menu->set_name("ArdourContextMenu");
459 RadioMenuItem::Group layers_group;
461 /* Find out how many overlaid/stacked tracks we have in the selection */
465 TrackSelection const & s = _editor.get_selection().tracks;
466 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
467 StreamView* v = (*i)->view ();
472 switch (v->layer_display ()) {
482 /* We're not connecting to signal_toggled() here; in the case where these two items are
483 set to be in the `inconsistent' state, it seems that one or other will end up active
484 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
485 select the active one, no toggled signal is emitted so nothing happens.
488 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
489 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
490 i->set_active (overlaid != 0 && stacked == 0);
491 i->set_inconsistent (overlaid != 0 && stacked != 0);
492 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
494 layers_items.push_back (
495 RadioMenuElem (layers_group, _("Stacked"),
496 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
499 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
500 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
501 i->set_active (overlaid == 0 && stacked != 0);
502 i->set_inconsistent (overlaid != 0 && stacked != 0);
504 items.push_back (MenuElem (_("Layers"), *layers_menu));
506 if (!Profile->get_sae()) {
508 Menu* alignment_menu = manage (new Menu);
509 MenuList& alignment_items = alignment_menu->items();
510 alignment_menu->set_name ("ArdourContextMenu");
512 RadioMenuItem::Group align_group;
514 /* Same verbose hacks as for the layering options above */
520 boost::shared_ptr<Track> first_track;
522 TrackSelection const & s = _editor.get_selection().tracks;
523 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
524 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
525 if (!r || !r->is_track ()) {
530 first_track = r->track();
533 switch (r->track()->alignment_choice()) {
537 switch (r->track()->alignment_style()) {
538 case ExistingMaterial:
546 case UseExistingMaterial:
562 inconsistent = false;
571 if (!inconsistent && first_track) {
573 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
574 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
575 i->set_active (automatic != 0 && existing == 0 && capture == 0);
576 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
578 switch (first_track->alignment_choice()) {
580 switch (first_track->alignment_style()) {
581 case ExistingMaterial:
582 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
585 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing != 0 && capture == 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
598 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
599 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
600 i->set_active (existing == 0 && capture != 0 && automatic == 0);
601 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
603 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
609 Menu* mode_menu = manage (new Menu);
610 MenuList& mode_items = mode_menu->items ();
611 mode_menu->set_name ("ArdourContextMenu");
613 RadioMenuItem::Group mode_group;
619 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
620 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
621 if (!r || !r->is_track ()) {
625 switch (r->track()->mode()) {
638 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
641 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
642 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
644 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
645 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
647 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
648 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
650 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
651 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
652 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
653 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
654 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
656 items.push_back (MenuElem (_("Mode"), *mode_menu));
659 color_mode_menu = build_color_mode_menu();
660 if (color_mode_menu) {
661 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
664 items.push_back (SeparatorElem());
666 build_playlist_menu ();
667 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
668 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
670 route_group_menu->detach ();
673 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
674 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
676 r.push_back (rtv->route ());
681 r.push_back (route ());
684 route_group_menu->build (r);
685 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
687 build_automation_action_menu (true);
688 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
690 items.push_back (SeparatorElem());
695 TrackSelection const & s = _editor.get_selection().tracks;
696 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
697 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
702 if (r->route()->active()) {
709 items.push_back (CheckMenuElem (_("Active")));
710 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
711 bool click_sets_active = true;
712 if (active > 0 && inactive == 0) {
713 i->set_active (true);
714 click_sets_active = false;
715 } else if (active > 0 && inactive > 0) {
716 i->set_inconsistent (true);
718 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
720 items.push_back (SeparatorElem());
721 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
722 if (!Profile->get_sae()) {
723 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
725 items.push_front (SeparatorElem());
726 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
731 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
733 if (apply_to_selection) {
734 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
739 if (!track()->can_use_mode (mode, needs_bounce)) {
745 cerr << "would bounce this one\n";
750 track()->set_mode (mode);
752 rec_enable_button->remove ();
755 case ARDOUR::NonLayered:
757 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
759 case ARDOUR::Destructive:
760 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
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 rec_enable_button->set_text (_("r"));
2353 if (_route && _route->solo_safe()) {
2354 solo_button->remove ();
2355 if (solo_safe_pixbuf == 0) {
2356 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2358 solo_button->set_image (solo_safe_pixbuf);
2359 solo_button->set_text (string());
2361 if (Config->get_solo_control_is_listen_control()) {
2362 switch (Config->get_listen_position()) {
2363 case AfterFaderListen:
2364 solo_button->set_text (_("A"));
2366 case PreFaderListen:
2367 solo_button->set_text (_("P"));
2371 solo_button->set_text (_("s"));
2374 mute_button->set_text (_("m"));
2378 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2380 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2381 if (i != _main_automation_menu_map.end()) {
2385 i = _subplugin_menu_map.find (param);
2386 if (i != _subplugin_menu_map.end()) {
2394 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2396 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2398 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2402 gain_track.reset (new AutomationTimeAxisView (_session,
2403 _route, _route->amp(), c, param,
2408 _route->amp()->describe_parameter(param)));
2411 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2414 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2418 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2420 l->push_back (rv->region());
2424 RouteTimeAxisView::combine_regions ()
2426 /* as of may 2011, we do not offer uncombine for MIDI tracks
2429 if (!is_audio_track()) {
2437 Playlist::RegionList selected_regions;
2438 boost::shared_ptr<Playlist> playlist = track()->playlist();
2440 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2442 if (selected_regions.size() < 2) {
2446 playlist->clear_changes ();
2447 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2449 _session->add_command (new StatefulDiffCommand (playlist));
2450 /* make the new region be selected */
2452 return _view->find_view (compound_region);
2456 RouteTimeAxisView::uncombine_regions ()
2458 /* as of may 2011, we do not offer uncombine for MIDI tracks
2460 if (!is_audio_track()) {
2468 Playlist::RegionList selected_regions;
2469 boost::shared_ptr<Playlist> playlist = track()->playlist();
2471 /* have to grab selected regions first because the uncombine is going
2472 * to change that in the middle of the list traverse
2475 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2477 playlist->clear_changes ();
2479 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2480 playlist->uncombine (*i);
2483 _session->add_command (new StatefulDiffCommand (playlist));
2487 RouteTimeAxisView::state_id() const
2489 return string_compose ("rtav %1", _route->id().to_s());