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 set_gui_property ("visible", "yes");
148 update_solo_display ();
150 timestretch_rect = 0;
153 ignore_toggle = false;
155 route_group_button.set_name ("TrackGroupButton");
156 playlist_button.set_name ("TrackPlaylistButton");
157 automation_button.set_name ("TrackAutomationButton");
159 route_group_button.unset_flags (Gtk::CAN_FOCUS);
160 playlist_button.unset_flags (Gtk::CAN_FOCUS);
161 automation_button.unset_flags (Gtk::CAN_FOCUS);
163 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
164 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
165 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 rec_enable_button->remove ();
173 switch (track()->mode()) {
175 case ARDOUR::NonLayered:
176 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
178 case ARDOUR::Destructive:
179 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
182 rec_enable_button->show_all ();
184 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
186 if (is_midi_track()) {
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
189 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
192 rec_enable_button->set_sensitive (_session->writable());
195 controls_hbox.pack_start(gm.get_level_meter(), false, false);
196 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
197 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
198 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
200 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
202 if (!_route->is_master()) {
203 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
207 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
210 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
211 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
212 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
213 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
217 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 if (is_track() && track()->mode() == ARDOUR::Normal) {
220 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
225 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
226 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
230 str = gui_property ("layer-display");
232 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
235 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
236 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
238 /* pick up the correct freeze state */
242 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
243 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
244 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
246 PropertyList* plist = new PropertyList();
248 plist->add (ARDOUR::Properties::edit, true);
249 plist->add (ARDOUR::Properties::mute, true);
250 plist->add (ARDOUR::Properties::solo, true);
252 route_group_menu = new RouteGroupMenu (_session, plist);
254 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
255 gm.get_gain_slider().set_name ("TrackGainFader");
261 RouteTimeAxisView::~RouteTimeAxisView ()
263 CatchDeletion (this);
265 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
269 delete playlist_action_menu;
270 playlist_action_menu = 0;
275 _automation_tracks.clear ();
277 delete route_group_menu;
281 RouteTimeAxisView::post_construct ()
283 /* map current state of the route */
285 update_diskstream_display ();
286 setup_processor_menu_and_curves ();
287 reset_processor_automation_curves ();
290 /** Set up the processor menu for the current set of processors, and
291 * display automation curves for any parameters which have data.
294 RouteTimeAxisView::setup_processor_menu_and_curves ()
296 _subplugin_menu_map.clear ();
297 subplugin_menu.items().clear ();
298 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
299 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
303 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
305 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
306 if (_route->route_group()) {
307 _route->route_group()->remove (_route);
313 r.push_back (route ());
315 route_group_menu->build (r);
316 route_group_menu->menu()->popup (ev->button, ev->time);
322 RouteTimeAxisView::playlist_changed ()
328 RouteTimeAxisView::label_view ()
330 string x = _route->name();
332 if (x != name_entry.get_text()) {
333 name_entry.set_text (x);
336 if (x != name_label.get_text()) {
337 name_label.set_text (x);
340 ARDOUR_UI::instance()->set_tip (name_entry, x);
344 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
346 if (what_changed.contains (ARDOUR::Properties::name)) {
352 RouteTimeAxisView::take_name_changed (void *src)
360 RouteTimeAxisView::playlist_click ()
362 build_playlist_menu ();
363 conditionally_add_to_selection ();
364 playlist_action_menu->popup (1, gtk_get_current_event_time());
368 RouteTimeAxisView::automation_click ()
370 conditionally_add_to_selection ();
371 build_automation_action_menu (false);
372 automation_action_menu->popup (1, gtk_get_current_event_time());
376 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
378 using namespace Menu_Helpers;
380 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
381 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
384 detach_menu (subplugin_menu);
386 _main_automation_menu_map.clear ();
387 delete automation_action_menu;
388 automation_action_menu = new Menu;
390 MenuList& items = automation_action_menu->items();
392 automation_action_menu->set_name ("ArdourContextMenu");
394 items.push_back (MenuElem (_("Show All Automation"),
395 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
397 items.push_back (MenuElem (_("Show Existing Automation"),
398 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
400 items.push_back (MenuElem (_("Hide All Automation"),
401 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
403 items.push_back (SeparatorElem ());
405 /* Attach the plugin submenu. It may have previously been used elsewhere,
406 so it was detached above */
408 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
409 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
413 RouteTimeAxisView::build_display_menu ()
415 using namespace Menu_Helpers;
419 TimeAxisView::build_display_menu ();
421 /* now fill it with our stuff */
423 MenuList& items = display_menu->items();
424 display_menu->set_name ("ArdourContextMenu");
426 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
429 detach_menu (*_size_menu);
432 items.push_back (MenuElem (_("Height"), *_size_menu));
434 items.push_back (SeparatorElem());
436 if (!Profile->get_sae()) {
437 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
438 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
439 items.push_back (SeparatorElem());
442 // Hook for derived classes to add type specific stuff
443 append_extra_display_menu_items ();
447 Menu* layers_menu = manage (new Menu);
448 MenuList &layers_items = layers_menu->items();
449 layers_menu->set_name("ArdourContextMenu");
451 RadioMenuItem::Group layers_group;
453 /* Find out how many overlaid/stacked tracks we have in the selection */
457 TrackSelection const & s = _editor.get_selection().tracks;
458 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
459 StreamView* v = (*i)->view ();
464 switch (v->layer_display ()) {
474 /* We're not connecting to signal_toggled() here; in the case where these two items are
475 set to be in the `inconsistent' state, it seems that one or other will end up active
476 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
477 select the active one, no toggled signal is emitted so nothing happens.
480 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
481 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
482 i->set_active (overlaid != 0 && stacked == 0);
483 i->set_inconsistent (overlaid != 0 && stacked != 0);
484 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
486 layers_items.push_back (
487 RadioMenuElem (layers_group, _("Stacked"),
488 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
491 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
492 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
493 i->set_active (overlaid == 0 && stacked != 0);
494 i->set_inconsistent (overlaid != 0 && stacked != 0);
496 items.push_back (MenuElem (_("Layers"), *layers_menu));
498 if (!Profile->get_sae()) {
500 Menu* alignment_menu = manage (new Menu);
501 MenuList& alignment_items = alignment_menu->items();
502 alignment_menu->set_name ("ArdourContextMenu");
504 RadioMenuItem::Group align_group;
506 /* Same verbose hacks as for the layering options above */
512 boost::shared_ptr<Track> first_track;
514 TrackSelection const & s = _editor.get_selection().tracks;
515 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
516 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
517 if (!r || !r->is_track ()) {
522 first_track = r->track();
525 switch (r->track()->alignment_choice()) {
529 switch (r->track()->alignment_style()) {
530 case ExistingMaterial:
538 case UseExistingMaterial:
554 inconsistent = false;
563 if (!inconsistent && first_track) {
565 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
566 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
567 i->set_active (automatic != 0 && existing == 0 && capture == 0);
568 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
570 switch (first_track->alignment_choice()) {
572 switch (first_track->alignment_style()) {
573 case ExistingMaterial:
574 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
577 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
585 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
586 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
587 i->set_active (existing != 0 && capture == 0 && automatic == 0);
588 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
590 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
591 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
592 i->set_active (existing == 0 && capture != 0 && automatic == 0);
593 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
595 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
601 Menu* mode_menu = manage (new Menu);
602 MenuList& mode_items = mode_menu->items ();
603 mode_menu->set_name ("ArdourContextMenu");
605 RadioMenuItem::Group mode_group;
611 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
612 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
613 if (!r || !r->is_track ()) {
617 switch (r->track()->mode()) {
630 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
631 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
632 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
633 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
634 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
636 mode_items.push_back (RadioMenuElem (mode_group, _("Tape 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::Destructive, true));
639 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
640 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
642 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered 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::NonLayered, true));
645 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
646 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
648 items.push_back (MenuElem (_("Mode"), *mode_menu));
651 color_mode_menu = build_color_mode_menu();
652 if (color_mode_menu) {
653 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
656 items.push_back (SeparatorElem());
658 build_playlist_menu ();
659 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
660 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
662 route_group_menu->detach ();
665 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
666 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
668 r.push_back (rtv->route ());
673 r.push_back (route ());
676 route_group_menu->build (r);
677 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
679 build_automation_action_menu (true);
680 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
682 items.push_back (SeparatorElem());
687 TrackSelection const & s = _editor.get_selection().tracks;
688 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
689 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
694 if (r->route()->active()) {
701 items.push_back (CheckMenuElem (_("Active")));
702 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
703 bool click_sets_active = true;
704 if (active > 0 && inactive == 0) {
705 i->set_active (true);
706 click_sets_active = false;
707 } else if (active > 0 && inactive > 0) {
708 i->set_inconsistent (true);
710 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
712 items.push_back (SeparatorElem());
713 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
714 if (!Profile->get_sae()) {
715 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
717 items.push_front (SeparatorElem());
718 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
723 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
725 if (apply_to_selection) {
726 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
731 if (!track()->can_use_mode (mode, needs_bounce)) {
737 cerr << "would bounce this one\n";
742 track()->set_mode (mode);
744 rec_enable_button->remove ();
747 case ARDOUR::NonLayered:
749 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
751 case ARDOUR::Destructive:
752 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
756 rec_enable_button->show_all ();
761 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
767 TimeAxisView::show_timestretch (start, end);
777 /* check that the time selection was made in our route, or our route group.
778 remember that route_group() == 0 implies the route is *not* in a edit group.
781 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
782 /* this doesn't apply to us */
786 /* ignore it if our edit group is not active */
788 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
793 if (timestretch_rect == 0) {
794 timestretch_rect = new SimpleRect (*canvas_display ());
795 timestretch_rect->property_x1() = 0.0;
796 timestretch_rect->property_y1() = 0.0;
797 timestretch_rect->property_x2() = 0.0;
798 timestretch_rect->property_y2() = 0.0;
799 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
800 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
803 timestretch_rect->show ();
804 timestretch_rect->raise_to_top ();
806 x1 = start / _editor.get_current_zoom();
807 x2 = (end - 1) / _editor.get_current_zoom();
808 y2 = current_height() - 2;
810 timestretch_rect->property_x1() = x1;
811 timestretch_rect->property_y1() = 1.0;
812 timestretch_rect->property_x2() = x2;
813 timestretch_rect->property_y2() = y2;
817 RouteTimeAxisView::hide_timestretch ()
819 TimeAxisView::hide_timestretch ();
821 if (timestretch_rect) {
822 timestretch_rect->hide ();
827 RouteTimeAxisView::show_selection (TimeSelection& ts)
831 /* ignore it if our edit group is not active or if the selection was started
832 in some other track or route group (remember that route_group() == 0 means
833 that the track is not in an route group).
836 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
837 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
843 TimeAxisView::show_selection (ts);
847 RouteTimeAxisView::set_height (uint32_t h)
850 bool height_changed = (height == 0) || (h != height);
851 gm.get_level_meter().setup_meters (gmlen);
853 TimeAxisView::set_height (h);
856 _view->set_height ((double) current_height());
859 if (height >= preset_height (HeightNormal)) {
863 gm.get_gain_slider().show();
865 if (!_route || _route->is_monitor()) {
870 if (rec_enable_button)
871 rec_enable_button->show();
873 route_group_button.show();
874 automation_button.show();
876 if (is_track() && track()->mode() == ARDOUR::Normal) {
877 playlist_button.show();
884 gm.get_gain_slider().hide();
886 if (!_route || _route->is_monitor()) {
891 if (rec_enable_button)
892 rec_enable_button->show();
894 route_group_button.hide ();
895 automation_button.hide ();
897 if (is_track() && track()->mode() == ARDOUR::Normal) {
898 playlist_button.hide ();
903 if (height_changed && !no_redraw) {
904 /* only emit the signal if the height really changed */
910 RouteTimeAxisView::set_color (Gdk::Color const & c)
912 RouteUI::set_color (c);
915 _view->apply_color (_color, StreamView::RegionColor);
920 RouteTimeAxisView::reset_samples_per_unit ()
922 set_samples_per_unit (_editor.get_current_zoom());
926 RouteTimeAxisView::horizontal_position_changed ()
929 _view->horizontal_position_changed ();
934 RouteTimeAxisView::set_samples_per_unit (double spu)
939 speed = track()->speed();
943 _view->set_samples_per_unit (spu * speed);
946 TimeAxisView::set_samples_per_unit (spu * speed);
950 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
952 if (!mitem->get_active()) {
953 /* this is one of the two calls made when these radio menu items change status. this one
954 is for the item that became inactive, and we want to ignore it.
959 if (apply_to_selection) {
960 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
963 track()->set_align_choice (choice);
969 RouteTimeAxisView::rename_current_playlist ()
971 ArdourPrompter prompter (true);
974 boost::shared_ptr<Track> tr = track();
975 if (!tr || tr->destructive()) {
979 boost::shared_ptr<Playlist> pl = tr->playlist();
984 prompter.set_title (_("Rename Playlist"));
985 prompter.set_prompt (_("New name for playlist:"));
986 prompter.set_initial_text (pl->name());
987 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
988 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
990 switch (prompter.run ()) {
991 case Gtk::RESPONSE_ACCEPT:
992 prompter.get_result (name);
1004 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1006 std::string ret (basename);
1008 std::string const group_string = "." + route_group()->name() + ".";
1010 // iterate through all playlists
1012 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1013 std::string tmp = (*i)->name();
1015 std::string::size_type idx = tmp.find(group_string);
1016 // find those which belong to this group
1017 if (idx != string::npos) {
1018 tmp = tmp.substr(idx + group_string.length());
1020 // and find the largest current number
1021 int x = atoi(tmp.c_str());
1022 if (x > maxnumber) {
1031 snprintf (buf, sizeof(buf), "%d", maxnumber);
1033 ret = this->name() + "." + route_group()->name () + "." + buf;
1039 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1043 boost::shared_ptr<Track> tr = track ();
1044 if (!tr || tr->destructive()) {
1048 boost::shared_ptr<const Playlist> pl = tr->playlist();
1055 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1056 name = resolve_new_group_playlist_name(name, playlists_before_op);
1059 while (_session->playlists->by_name(name)) {
1060 name = Playlist::bump_name (name, *_session);
1063 // TODO: The prompter "new" button should be de-activated if the user
1064 // specifies a playlist name which already exists in the session.
1068 ArdourPrompter prompter (true);
1070 prompter.set_title (_("New Copy Playlist"));
1071 prompter.set_prompt (_("Name for new playlist:"));
1072 prompter.set_initial_text (name);
1073 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1074 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1075 prompter.show_all ();
1077 switch (prompter.run ()) {
1078 case Gtk::RESPONSE_ACCEPT:
1079 prompter.get_result (name);
1087 if (name.length()) {
1088 tr->use_copy_playlist ();
1089 tr->playlist()->set_name (name);
1094 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1098 boost::shared_ptr<Track> tr = track ();
1099 if (!tr || tr->destructive()) {
1103 boost::shared_ptr<const Playlist> pl = tr->playlist();
1110 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1111 name = resolve_new_group_playlist_name(name,playlists_before_op);
1114 while (_session->playlists->by_name(name)) {
1115 name = Playlist::bump_name (name, *_session);
1121 ArdourPrompter prompter (true);
1123 prompter.set_title (_("New Playlist"));
1124 prompter.set_prompt (_("Name for new playlist:"));
1125 prompter.set_initial_text (name);
1126 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1127 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1129 switch (prompter.run ()) {
1130 case Gtk::RESPONSE_ACCEPT:
1131 prompter.get_result (name);
1139 if (name.length()) {
1140 tr->use_new_playlist ();
1141 tr->playlist()->set_name (name);
1146 RouteTimeAxisView::clear_playlist ()
1148 boost::shared_ptr<Track> tr = track ();
1149 if (!tr || tr->destructive()) {
1153 boost::shared_ptr<Playlist> pl = tr->playlist();
1158 _editor.clear_playlist (pl);
1162 RouteTimeAxisView::speed_changed ()
1164 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1168 RouteTimeAxisView::update_diskstream_display ()
1178 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1180 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1182 /* special case: select/deselect all tracks */
1183 if (_editor.get_selection().selected (this)) {
1184 _editor.get_selection().clear_tracks ();
1186 _editor.select_all_tracks ();
1192 switch (ArdourKeyboard::selection_type (ev->state)) {
1193 case Selection::Toggle:
1194 _editor.get_selection().toggle (this);
1197 case Selection::Set:
1198 _editor.get_selection().set (this);
1201 case Selection::Extend:
1202 _editor.extend_selection_to_track (*this);
1205 case Selection::Add:
1206 _editor.get_selection().add (this);
1212 RouteTimeAxisView::set_selected_points (PointSelection& points)
1214 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1215 (*i)->set_selected_points (points);
1220 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1223 _view->set_selected_regionviews (regions);
1227 /** Add the selectable things that we have to a list.
1228 * @param results List to add things to.
1231 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1236 speed = track()->speed();
1239 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1240 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1242 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1243 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1246 /* pick up visible automation tracks */
1248 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1249 if (!(*i)->hidden()) {
1250 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1256 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1259 _view->get_inverted_selectables (sel, results);
1262 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1263 if (!(*i)->hidden()) {
1264 (*i)->get_inverted_selectables (sel, results);
1272 RouteTimeAxisView::route_group () const
1274 return _route->route_group();
1278 RouteTimeAxisView::name() const
1280 return _route->name();
1283 boost::shared_ptr<Playlist>
1284 RouteTimeAxisView::playlist () const
1286 boost::shared_ptr<Track> tr;
1288 if ((tr = track()) != 0) {
1289 return tr->playlist();
1291 return boost::shared_ptr<Playlist> ();
1296 RouteTimeAxisView::name_entry_changed ()
1300 x = name_entry.get_text ();
1302 if (x == _route->name()) {
1306 strip_whitespace_edges(x);
1308 if (x.length() == 0) {
1309 name_entry.set_text (_route->name());
1313 if (!_session->route_name_unique (x)) {
1314 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1315 name_entry.set_text (_route->name());
1316 } else if (_session->route_name_internal (x)) {
1317 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1319 name_entry.set_text (_route->name());
1321 _route->set_name (x);
1325 boost::shared_ptr<Region>
1326 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1328 boost::shared_ptr<Playlist> pl = playlist ();
1331 return pl->find_next_region (pos, point, dir);
1334 return boost::shared_ptr<Region> ();
1338 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1340 boost::shared_ptr<Playlist> pl = playlist ();
1343 return pl->find_next_region_boundary (pos, dir);
1350 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1352 boost::shared_ptr<Playlist> what_we_got;
1353 boost::shared_ptr<Track> tr = track ();
1354 boost::shared_ptr<Playlist> playlist;
1357 /* route is a bus, not a track */
1361 playlist = tr->playlist();
1363 TimeSelection time (selection.time);
1364 float const speed = tr->speed();
1365 if (speed != 1.0f) {
1366 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1367 (*i).start = session_frame_to_track_frame((*i).start, speed);
1368 (*i).end = session_frame_to_track_frame((*i).end, speed);
1372 playlist->clear_changes ();
1373 playlist->clear_owned_changes ();
1377 if (playlist->cut (time) != 0) {
1378 vector<Command*> cmds;
1379 playlist->rdiff (cmds);
1380 _session->add_commands (cmds);
1382 _session->add_command (new StatefulDiffCommand (playlist));
1387 if ((what_we_got = playlist->cut (time)) != 0) {
1388 _editor.get_cut_buffer().add (what_we_got);
1389 vector<Command*> cmds;
1390 playlist->rdiff (cmds);
1391 _session->add_commands (cmds);
1393 _session->add_command (new StatefulDiffCommand (playlist));
1397 if ((what_we_got = playlist->copy (time)) != 0) {
1398 _editor.get_cut_buffer().add (what_we_got);
1403 if ((what_we_got = playlist->cut (time)) != 0) {
1405 vector<Command*> cmds;
1406 playlist->rdiff (cmds);
1407 _session->add_commands (cmds);
1408 _session->add_command (new StatefulDiffCommand (playlist));
1409 what_we_got->release ();
1416 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1422 boost::shared_ptr<Playlist> pl = playlist ();
1423 PlaylistSelection::iterator p;
1425 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1427 if (p == selection.playlists.end()) {
1431 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1433 if (track()->speed() != 1.0f) {
1434 pos = session_frame_to_track_frame (pos, track()->speed());
1435 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1438 pl->clear_changes ();
1439 pl->paste (*p, pos, times);
1440 _session->add_command (new StatefulDiffCommand (pl));
1446 struct PlaylistSorter {
1447 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1448 return a->sort_id() < b->sort_id();
1453 RouteTimeAxisView::build_playlist_menu ()
1455 using namespace Menu_Helpers;
1461 delete playlist_action_menu;
1462 playlist_action_menu = new Menu;
1463 playlist_action_menu->set_name ("ArdourContextMenu");
1465 MenuList& playlist_items = playlist_action_menu->items();
1466 playlist_action_menu->set_name ("ArdourContextMenu");
1467 playlist_items.clear();
1469 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1470 boost::shared_ptr<Track> tr = track();
1471 RadioMenuItem::Group playlist_group;
1473 _session->playlists->get (playlists);
1475 /* find the playlists for this diskstream */
1476 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1477 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1478 playlists_tr.push_back(*i);
1482 /* sort the playlists */
1484 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1486 /* add the playlists to the menu */
1487 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1488 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1489 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1490 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1492 if (tr->playlist()->id() == (*i)->id()) {
1498 playlist_items.push_back (SeparatorElem());
1499 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1500 playlist_items.push_back (SeparatorElem());
1502 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1503 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1504 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1507 // Use a label which tells the user what is happening
1508 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1509 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1513 playlist_items.push_back (SeparatorElem());
1514 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1515 playlist_items.push_back (SeparatorElem());
1517 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1521 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1523 assert (is_track());
1525 // exit if we were triggered by deactivating the old playlist
1526 if (!item->get_active()) {
1530 boost::shared_ptr<Playlist> pl (wpl.lock());
1536 if (track()->playlist() == pl) {
1537 // exit when use_playlist is called by the creation of the playlist menu
1538 // or the playlist choice is unchanged
1542 track()->use_playlist (pl);
1544 RouteGroup* rg = route_group();
1546 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1547 std::string group_string = "." + rg->name() + ".";
1549 std::string take_name = pl->name();
1550 std::string::size_type idx = take_name.find(group_string);
1552 if (idx == std::string::npos)
1555 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1557 boost::shared_ptr<RouteList> rl (rg->route_list());
1559 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1560 if ( (*i) == this->route()) {
1564 std::string playlist_name = (*i)->name()+group_string+take_name;
1566 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1571 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1573 // No playlist for this track for this take yet, make it
1574 track->use_new_playlist();
1575 track->playlist()->set_name(playlist_name);
1577 track->use_playlist(ipl);
1584 RouteTimeAxisView::show_playlist_selector ()
1586 _editor.playlist_selector().show_for (this);
1590 RouteTimeAxisView::map_frozen ()
1596 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1598 switch (track()->freeze_state()) {
1600 playlist_button.set_sensitive (false);
1601 rec_enable_button->set_sensitive (false);
1604 playlist_button.set_sensitive (true);
1605 rec_enable_button->set_sensitive (true);
1611 RouteTimeAxisView::color_handler ()
1613 //case cTimeStretchOutline:
1614 if (timestretch_rect) {
1615 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1617 //case cTimeStretchFill:
1618 if (timestretch_rect) {
1619 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1625 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1626 * Will add track if necessary.
1629 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1631 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1632 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1635 /* it doesn't exist yet, so we don't care about the button state: just add it */
1636 create_automation_child (param, true);
1639 bool yn = menu->get_active();
1640 bool changed = false;
1642 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1644 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1645 will have done that for us.
1648 if (changed && !no_redraw) {
1656 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1658 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1664 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1666 if (menu && !_hidden) {
1667 ignore_toggle = true;
1668 menu->set_active (false);
1669 ignore_toggle = false;
1672 if (_route && !no_redraw) {
1679 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1681 if (apply_to_selection) {
1682 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1686 /* Show our automation */
1688 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1689 i->second->set_marked_for_display (true);
1691 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1694 menu->set_active(true);
1699 /* Show processor automation */
1701 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1702 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1703 if ((*ii)->view == 0) {
1704 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1707 (*ii)->menu_item->set_active (true);
1720 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1722 if (apply_to_selection) {
1723 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1727 /* Show our automation */
1729 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1730 if (i->second->has_automation()) {
1731 i->second->set_marked_for_display (true);
1733 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1735 menu->set_active(true);
1740 /* Show processor automation */
1742 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1743 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1744 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1745 (*ii)->menu_item->set_active (true);
1757 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1759 if (apply_to_selection) {
1760 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1764 /* Hide our automation */
1766 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1767 i->second->set_marked_for_display (false);
1769 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1772 menu->set_active (false);
1776 /* Hide processor automation */
1778 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1779 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1780 (*ii)->menu_item->set_active (false);
1791 RouteTimeAxisView::region_view_added (RegionView* rv)
1793 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1794 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1795 boost::shared_ptr<AutomationTimeAxisView> atv;
1797 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1802 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1803 (*i)->add_ghost(rv);
1807 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1809 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1815 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1817 parent.remove_processor_automation_node (this);
1821 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1824 remove_child (pan->view);
1828 RouteTimeAxisView::ProcessorAutomationNode*
1829 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1831 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1833 if ((*i)->processor == processor) {
1835 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1836 if ((*ii)->what == what) {
1846 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1848 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1851 ProcessorAutomationNode* pan;
1853 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1854 /* session state may never have been saved with new plugin */
1855 error << _("programming error: ")
1856 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1857 processor->name(), what.type(), (int) what.channel(), what.id() )
1867 boost::shared_ptr<AutomationControl> control
1868 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1870 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1871 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1872 _editor, *this, false, parent_canvas,
1873 processor->describe_parameter (what), processor->name()));
1875 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1877 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1880 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1885 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1888 pan->menu_item->set_active (false);
1897 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1899 boost::shared_ptr<Processor> processor (p.lock ());
1901 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1902 /* The Amp processor is a special case and is dealt with separately */
1906 set<Evoral::Parameter> existing;
1908 processor->what_has_data (existing);
1910 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1912 Evoral::Parameter param (*i);
1913 boost::shared_ptr<AutomationLine> al;
1915 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1918 add_processor_automation_curve (processor, param);
1924 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1926 using namespace Menu_Helpers;
1930 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1932 _automation_tracks[param] = track;
1934 /* existing state overrides "show" argument */
1935 string s = track->gui_property ("visible");
1937 show = string_is_affirmative (s);
1940 /* this might or might not change the visibility status, so don't rely on it */
1941 track->set_marked_for_display (show);
1943 if (show && !no_redraw) {
1947 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1948 /* MIDI-related parameters are always in the menu, there's no
1949 reason to rebuild the menu just because we added a automation
1950 lane for one of them. But if we add a non-MIDI automation
1951 lane, then we need to invalidate the display menu.
1953 delete display_menu;
1959 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1961 boost::shared_ptr<Processor> processor (p.lock ());
1963 if (!processor || !processor->display_to_user ()) {
1967 /* we use this override to veto the Amp processor from the plugin menu,
1968 as its automation lane can be accessed using the special "Fader" menu
1972 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1976 using namespace Menu_Helpers;
1977 ProcessorAutomationInfo *rai;
1978 list<ProcessorAutomationInfo*>::iterator x;
1980 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1982 if (automatable.empty()) {
1986 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1987 if ((*x)->processor == processor) {
1992 if (x == processor_automation.end()) {
1994 rai = new ProcessorAutomationInfo (processor);
1995 processor_automation.push_back (rai);
2003 /* any older menu was deleted at the top of processors_changed()
2004 when we cleared the subplugin menu.
2007 rai->menu = manage (new Menu);
2008 MenuList& items = rai->menu->items();
2009 rai->menu->set_name ("ArdourContextMenu");
2013 std::set<Evoral::Parameter> has_visible_automation;
2014 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2016 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2018 ProcessorAutomationNode* pan;
2019 CheckMenuItem* mitem;
2021 string name = processor->describe_parameter (*i);
2023 items.push_back (CheckMenuElem (name));
2024 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2026 _subplugin_menu_map[*i] = mitem;
2028 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2029 mitem->set_active(true);
2032 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2036 pan = new ProcessorAutomationNode (*i, mitem, *this);
2038 rai->lines.push_back (pan);
2042 pan->menu_item = mitem;
2046 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2049 /* add the menu for this processor, because the subplugin
2050 menu is always cleared at the top of processors_changed().
2051 this is the result of some poor design in gtkmm and/or
2055 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2060 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2061 RouteTimeAxisView::ProcessorAutomationNode* pan)
2063 bool showit = pan->menu_item->get_active();
2064 bool redraw = false;
2066 if (pan->view == 0 && showit) {
2067 add_processor_automation_curve (rai->processor, pan->what);
2071 if (pan->view && pan->view->set_marked_for_display (showit)) {
2075 if (redraw && !no_redraw) {
2081 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2083 if (c.type == RouteProcessorChange::MeterPointChange) {
2084 /* nothing to do if only the meter point has changed */
2088 using namespace Menu_Helpers;
2090 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2091 (*i)->valid = false;
2094 setup_processor_menu_and_curves ();
2096 bool deleted_processor_automation = false;
2098 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2100 list<ProcessorAutomationInfo*>::iterator tmp;
2108 processor_automation.erase (i);
2109 deleted_processor_automation = true;
2116 if (deleted_processor_automation && !no_redraw) {
2121 boost::shared_ptr<AutomationLine>
2122 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2124 ProcessorAutomationNode* pan;
2126 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2132 return boost::shared_ptr<AutomationLine>();
2136 RouteTimeAxisView::reset_processor_automation_curves ()
2138 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2144 RouteTimeAxisView::update_rec_display ()
2146 RouteUI::update_rec_display ();
2147 name_entry.set_sensitive (!_route->record_enabled());
2151 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2153 if (apply_to_selection) {
2154 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2158 _view->set_layer_display (d);
2161 set_gui_property (X_("layer-display"), enum_2_string (d));
2166 RouteTimeAxisView::layer_display () const
2169 return _view->layer_display ();
2172 /* we don't know, since we don't have a _view, so just return something */
2178 boost::shared_ptr<AutomationTimeAxisView>
2179 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2181 AutomationTracks::iterator i = _automation_tracks.find(param);
2182 if (i != _automation_tracks.end()) {
2185 return boost::shared_ptr<AutomationTimeAxisView>();
2190 RouteTimeAxisView::fast_update ()
2192 gm.get_level_meter().update_meters ();
2196 RouteTimeAxisView::hide_meter ()
2199 gm.get_level_meter().hide_meters ();
2203 RouteTimeAxisView::show_meter ()
2209 RouteTimeAxisView::reset_meter ()
2211 if (Config->get_show_track_meters()) {
2212 gm.get_level_meter().setup_meters (height-5);
2219 RouteTimeAxisView::clear_meter ()
2221 gm.get_level_meter().clear_meters ();
2225 RouteTimeAxisView::meter_changed ()
2227 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2232 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2238 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2240 using namespace Menu_Helpers;
2242 if (!_underlay_streams.empty()) {
2243 MenuList& parent_items = parent_menu->items();
2244 Menu* gs_menu = manage (new Menu);
2245 gs_menu->set_name ("ArdourContextMenu");
2246 MenuList& gs_items = gs_menu->items();
2248 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2250 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2251 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2252 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2258 RouteTimeAxisView::set_underlay_state()
2260 if (!underlay_xml_node) {
2264 XMLNodeList nlist = underlay_xml_node->children();
2265 XMLNodeConstIterator niter;
2266 XMLNode *child_node;
2268 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2269 child_node = *niter;
2271 if (child_node->name() != "Underlay") {
2275 XMLProperty* prop = child_node->property ("id");
2277 PBD::ID id (prop->value());
2279 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2282 add_underlay(v->view(), false);
2291 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2297 RouteTimeAxisView& other = v->trackview();
2299 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2300 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2301 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2305 _underlay_streams.push_back(v);
2306 other._underlay_mirrors.push_back(this);
2308 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2310 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2312 if (!underlay_xml_node) {
2313 underlay_xml_node = xml_node->add_child("Underlays");
2316 XMLNode* node = underlay_xml_node->add_child("Underlay");
2317 XMLProperty* prop = node->add_property("id");
2318 prop->set_value(v->trackview().route()->id().to_s());
2325 RouteTimeAxisView::remove_underlay (StreamView* v)
2331 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2332 RouteTimeAxisView& other = v->trackview();
2334 if (it != _underlay_streams.end()) {
2335 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2337 if (gm == other._underlay_mirrors.end()) {
2338 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2342 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2344 _underlay_streams.erase(it);
2345 other._underlay_mirrors.erase(gm);
2347 if (underlay_xml_node) {
2348 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2354 RouteTimeAxisView::set_button_names ()
2356 rec_enable_button_label.set_text (_("r"));
2358 if (_route && _route->solo_safe()) {
2359 solo_button_label.set_text (X_("!"));
2361 if (Config->get_solo_control_is_listen_control()) {
2362 switch (Config->get_listen_position()) {
2363 case AfterFaderListen:
2364 solo_button_label.set_text (_("A"));
2366 case PreFaderListen:
2367 solo_button_label.set_text (_("P"));
2371 solo_button_label.set_text (_("s"));
2374 mute_button_label.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());