2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/audioplaylist.h"
47 #include "ardour/diskstream.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/ladspa_plugin.h"
50 #include "ardour/location.h"
51 #include "ardour/panner.h"
52 #include "ardour/playlist.h"
53 #include "ardour/playlist.h"
54 #include "ardour/processor.h"
55 #include "ardour/profile.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
65 #include "ardour_button.h"
67 #include "global_signals.h"
68 #include "route_time_axis.h"
69 #include "automation_time_axis.h"
70 #include "canvas_impl.h"
71 #include "crossfade_view.h"
73 #include "gui_thread.h"
75 #include "playlist_selector.h"
76 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "simplerect.h"
83 #include "streamview.h"
85 #include "route_group_menu.h"
87 #include "ardour/track.h"
91 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
95 using namespace Editing;
99 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
102 RouteTimeAxisView::setup_slider_pix ()
104 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
105 throw failed_constructor ();
109 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
112 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
114 , parent_canvas (canvas)
116 , button_table (3, 3)
117 , route_group_button (_("g"))
118 , playlist_button (_("p"))
119 , automation_button (_("a"))
120 , automation_action_menu (0)
121 , plugins_submenu_item (0)
122 , route_group_menu (0)
123 , playlist_action_menu (0)
125 , color_mode_menu (0)
126 , gm (sess, slider, true, 115)
131 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
133 RouteUI::set_route (rt);
135 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
136 gm.get_level_meter().set_no_show_all();
137 gm.get_level_meter().setup_meters(50);
139 string str = gui_property ("height");
141 set_height (atoi (str));
143 set_height (preset_height (HeightNormal));
146 if (!_route->is_hidden()) {
147 if (gui_property ("visible").empty()) {
148 set_gui_property ("visible", true);
151 set_gui_property ("visible", false);
155 update_solo_display ();
157 timestretch_rect = 0;
160 ignore_toggle = false;
162 route_group_button.set_name ("route button");
163 playlist_button.set_name ("route button");
164 automation_button.set_name ("route button");
166 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
167 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
168 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
174 switch (track()->mode()) {
176 case ARDOUR::NonLayered:
177 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
179 case ARDOUR::Destructive:
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
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");
257 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
263 RouteTimeAxisView::~RouteTimeAxisView ()
265 CatchDeletion (this);
267 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
271 delete playlist_action_menu;
272 playlist_action_menu = 0;
277 _automation_tracks.clear ();
279 delete route_group_menu;
283 RouteTimeAxisView::post_construct ()
285 /* map current state of the route */
287 update_diskstream_display ();
288 setup_processor_menu_and_curves ();
289 reset_processor_automation_curves ();
292 /** Set up the processor menu for the current set of processors, and
293 * display automation curves for any parameters which have data.
296 RouteTimeAxisView::setup_processor_menu_and_curves ()
298 _subplugin_menu_map.clear ();
299 subplugin_menu.items().clear ();
300 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
301 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
305 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
307 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
308 if (_route->route_group()) {
309 _route->route_group()->remove (_route);
315 r.push_back (route ());
317 route_group_menu->build (r);
318 route_group_menu->menu()->popup (ev->button, ev->time);
324 RouteTimeAxisView::playlist_changed ()
330 RouteTimeAxisView::label_view ()
332 string x = _route->name();
334 if (x != name_entry.get_text()) {
335 name_entry.set_text (x);
338 if (x != name_label.get_text()) {
339 name_label.set_text (x);
342 ARDOUR_UI::instance()->set_tip (name_entry, x);
346 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
348 if (what_changed.contains (ARDOUR::Properties::name)) {
354 RouteTimeAxisView::take_name_changed (void *src)
362 RouteTimeAxisView::playlist_click ()
364 build_playlist_menu ();
365 conditionally_add_to_selection ();
366 playlist_action_menu->popup (1, gtk_get_current_event_time());
370 RouteTimeAxisView::automation_click ()
372 conditionally_add_to_selection ();
373 build_automation_action_menu (false);
374 automation_action_menu->popup (1, gtk_get_current_event_time());
378 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
380 using namespace Menu_Helpers;
382 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
383 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
386 detach_menu (subplugin_menu);
388 _main_automation_menu_map.clear ();
389 delete automation_action_menu;
390 automation_action_menu = new Menu;
392 MenuList& items = automation_action_menu->items();
394 automation_action_menu->set_name ("ArdourContextMenu");
396 items.push_back (MenuElem (_("Show All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
399 items.push_back (MenuElem (_("Show Existing Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
402 items.push_back (MenuElem (_("Hide All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
405 items.push_back (SeparatorElem ());
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above */
410 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
411 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
415 RouteTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers;
421 TimeAxisView::build_display_menu ();
423 /* now fill it with our stuff */
425 MenuList& items = display_menu->items();
426 display_menu->set_name ("ArdourContextMenu");
428 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
431 detach_menu (*_size_menu);
434 items.push_back (MenuElem (_("Height"), *_size_menu));
436 items.push_back (SeparatorElem());
438 if (!Profile->get_sae()) {
439 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
440 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
441 items.push_back (SeparatorElem());
444 // Hook for derived classes to add type specific stuff
445 append_extra_display_menu_items ();
449 Menu* layers_menu = manage (new Menu);
450 MenuList &layers_items = layers_menu->items();
451 layers_menu->set_name("ArdourContextMenu");
453 RadioMenuItem::Group layers_group;
455 /* Find out how many overlaid/stacked tracks we have in the selection */
459 TrackSelection const & s = _editor.get_selection().tracks;
460 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
461 StreamView* v = (*i)->view ();
466 switch (v->layer_display ()) {
477 /* We're not connecting to signal_toggled() here; in the case where these two items are
478 set to be in the `inconsistent' state, it seems that one or other will end up active
479 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
480 select the active one, no toggled signal is emitted so nothing happens.
483 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
484 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
485 i->set_active (overlaid != 0 && stacked == 0);
486 i->set_inconsistent (overlaid != 0 && stacked != 0);
487 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
489 layers_items.push_back (
490 RadioMenuElem (layers_group, _("Stacked"),
491 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
494 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
495 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
496 i->set_active (overlaid == 0 && stacked != 0);
497 i->set_inconsistent (overlaid != 0 && stacked != 0);
499 items.push_back (MenuElem (_("Layers"), *layers_menu));
501 if (!Profile->get_sae()) {
503 Menu* alignment_menu = manage (new Menu);
504 MenuList& alignment_items = alignment_menu->items();
505 alignment_menu->set_name ("ArdourContextMenu");
507 RadioMenuItem::Group align_group;
509 /* Same verbose hacks as for the layering options above */
515 boost::shared_ptr<Track> first_track;
517 TrackSelection const & s = _editor.get_selection().tracks;
518 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
519 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
520 if (!r || !r->is_track ()) {
525 first_track = r->track();
528 switch (r->track()->alignment_choice()) {
532 switch (r->track()->alignment_style()) {
533 case ExistingMaterial:
541 case UseExistingMaterial:
557 inconsistent = false;
566 if (!inconsistent && first_track) {
568 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
569 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
570 i->set_active (automatic != 0 && existing == 0 && capture == 0);
571 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
573 switch (first_track->alignment_choice()) {
575 switch (first_track->alignment_style()) {
576 case ExistingMaterial:
577 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
580 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
588 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
589 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
590 i->set_active (existing != 0 && capture == 0 && automatic == 0);
591 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
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, UseCaptureTime, true));
598 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
604 Menu* mode_menu = manage (new Menu);
605 MenuList& mode_items = mode_menu->items ();
606 mode_menu->set_name ("ArdourContextMenu");
608 RadioMenuItem::Group mode_group;
614 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
615 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
616 if (!r || !r->is_track ()) {
620 switch (r->track()->mode()) {
633 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
636 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
637 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
639 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
640 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
642 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
643 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
645 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
646 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
648 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
649 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
651 items.push_back (MenuElem (_("Mode"), *mode_menu));
654 color_mode_menu = build_color_mode_menu();
655 if (color_mode_menu) {
656 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
659 items.push_back (SeparatorElem());
661 build_playlist_menu ();
662 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
663 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
665 route_group_menu->detach ();
668 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
669 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
671 r.push_back (rtv->route ());
676 r.push_back (route ());
679 route_group_menu->build (r);
680 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
682 build_automation_action_menu (true);
683 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
685 items.push_back (SeparatorElem());
690 TrackSelection const & s = _editor.get_selection().tracks;
691 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
692 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
697 if (r->route()->active()) {
704 items.push_back (CheckMenuElem (_("Active")));
705 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
706 bool click_sets_active = true;
707 if (active > 0 && inactive == 0) {
708 i->set_active (true);
709 click_sets_active = false;
710 } else if (active > 0 && inactive > 0) {
711 i->set_inconsistent (true);
713 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
715 items.push_back (SeparatorElem());
716 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
717 if (!Profile->get_sae()) {
718 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
720 items.push_front (SeparatorElem());
721 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
726 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
728 if (apply_to_selection) {
729 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
734 if (!track()->can_use_mode (mode, needs_bounce)) {
740 cerr << "would bounce this one\n";
745 track()->set_mode (mode);
747 rec_enable_button->remove ();
750 case ARDOUR::NonLayered:
752 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
753 rec_enable_button->set_text (string());
755 case ARDOUR::Destructive:
756 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
757 rec_enable_button->set_text (string());
761 rec_enable_button->show_all ();
766 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
772 TimeAxisView::show_timestretch (start, end);
782 /* check that the time selection was made in our route, or our route group.
783 remember that route_group() == 0 implies the route is *not* in a edit group.
786 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
787 /* this doesn't apply to us */
791 /* ignore it if our edit group is not active */
793 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
798 if (timestretch_rect == 0) {
799 timestretch_rect = new SimpleRect (*canvas_display ());
800 timestretch_rect->property_x1() = 0.0;
801 timestretch_rect->property_y1() = 0.0;
802 timestretch_rect->property_x2() = 0.0;
803 timestretch_rect->property_y2() = 0.0;
804 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
805 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
808 timestretch_rect->show ();
809 timestretch_rect->raise_to_top ();
811 x1 = start / _editor.get_current_zoom();
812 x2 = (end - 1) / _editor.get_current_zoom();
813 y2 = current_height() - 2;
815 timestretch_rect->property_x1() = x1;
816 timestretch_rect->property_y1() = 1.0;
817 timestretch_rect->property_x2() = x2;
818 timestretch_rect->property_y2() = y2;
822 RouteTimeAxisView::hide_timestretch ()
824 TimeAxisView::hide_timestretch ();
826 if (timestretch_rect) {
827 timestretch_rect->hide ();
832 RouteTimeAxisView::show_selection (TimeSelection& ts)
836 /* ignore it if our edit group is not active or if the selection was started
837 in some other track or route group (remember that route_group() == 0 means
838 that the track is not in an route group).
841 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
842 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
848 TimeAxisView::show_selection (ts);
852 RouteTimeAxisView::set_height (uint32_t h)
855 bool height_changed = (height == 0) || (h != height);
856 gm.get_level_meter().setup_meters (gmlen);
858 TimeAxisView::set_height (h);
861 _view->set_height ((double) current_height());
864 if (height >= preset_height (HeightNormal)) {
868 gm.get_gain_slider().show();
870 if (!_route || _route->is_monitor()) {
875 if (rec_enable_button)
876 rec_enable_button->show();
878 route_group_button.show();
879 automation_button.show();
881 if (is_track() && track()->mode() == ARDOUR::Normal) {
882 playlist_button.show();
889 gm.get_gain_slider().hide();
891 if (!_route || _route->is_monitor()) {
896 if (rec_enable_button)
897 rec_enable_button->show();
899 route_group_button.hide ();
900 automation_button.hide ();
902 if (is_track() && track()->mode() == ARDOUR::Normal) {
903 playlist_button.hide ();
908 if (height_changed && !no_redraw) {
909 /* only emit the signal if the height really changed */
915 RouteTimeAxisView::route_color_changed ()
918 _view->apply_color (color(), StreamView::RegionColor);
923 RouteTimeAxisView::reset_samples_per_unit ()
925 set_samples_per_unit (_editor.get_current_zoom());
929 RouteTimeAxisView::horizontal_position_changed ()
932 _view->horizontal_position_changed ();
937 RouteTimeAxisView::set_samples_per_unit (double spu)
942 speed = track()->speed();
946 _view->set_samples_per_unit (spu * speed);
949 TimeAxisView::set_samples_per_unit (spu * speed);
953 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
955 if (!mitem->get_active()) {
956 /* this is one of the two calls made when these radio menu items change status. this one
957 is for the item that became inactive, and we want to ignore it.
962 if (apply_to_selection) {
963 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
966 track()->set_align_choice (choice);
972 RouteTimeAxisView::rename_current_playlist ()
974 ArdourPrompter prompter (true);
977 boost::shared_ptr<Track> tr = track();
978 if (!tr || tr->destructive()) {
982 boost::shared_ptr<Playlist> pl = tr->playlist();
987 prompter.set_title (_("Rename Playlist"));
988 prompter.set_prompt (_("New name for playlist:"));
989 prompter.set_initial_text (pl->name());
990 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
991 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
993 switch (prompter.run ()) {
994 case Gtk::RESPONSE_ACCEPT:
995 prompter.get_result (name);
1007 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1009 std::string ret (basename);
1011 std::string const group_string = "." + route_group()->name() + ".";
1013 // iterate through all playlists
1015 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1016 std::string tmp = (*i)->name();
1018 std::string::size_type idx = tmp.find(group_string);
1019 // find those which belong to this group
1020 if (idx != string::npos) {
1021 tmp = tmp.substr(idx + group_string.length());
1023 // and find the largest current number
1024 int x = atoi(tmp.c_str());
1025 if (x > maxnumber) {
1034 snprintf (buf, sizeof(buf), "%d", maxnumber);
1036 ret = this->name() + "." + route_group()->name () + "." + buf;
1042 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1046 boost::shared_ptr<Track> tr = track ();
1047 if (!tr || tr->destructive()) {
1051 boost::shared_ptr<const Playlist> pl = tr->playlist();
1058 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1059 name = resolve_new_group_playlist_name(name, playlists_before_op);
1062 while (_session->playlists->by_name(name)) {
1063 name = Playlist::bump_name (name, *_session);
1066 // TODO: The prompter "new" button should be de-activated if the user
1067 // specifies a playlist name which already exists in the session.
1071 ArdourPrompter prompter (true);
1073 prompter.set_title (_("New Copy Playlist"));
1074 prompter.set_prompt (_("Name for new playlist:"));
1075 prompter.set_initial_text (name);
1076 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1077 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1078 prompter.show_all ();
1080 switch (prompter.run ()) {
1081 case Gtk::RESPONSE_ACCEPT:
1082 prompter.get_result (name);
1090 if (name.length()) {
1091 tr->use_copy_playlist ();
1092 tr->playlist()->set_name (name);
1097 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1101 boost::shared_ptr<Track> tr = track ();
1102 if (!tr || tr->destructive()) {
1106 boost::shared_ptr<const Playlist> pl = tr->playlist();
1113 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1114 name = resolve_new_group_playlist_name(name,playlists_before_op);
1117 while (_session->playlists->by_name(name)) {
1118 name = Playlist::bump_name (name, *_session);
1124 ArdourPrompter prompter (true);
1126 prompter.set_title (_("New Playlist"));
1127 prompter.set_prompt (_("Name for new playlist:"));
1128 prompter.set_initial_text (name);
1129 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1130 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1132 switch (prompter.run ()) {
1133 case Gtk::RESPONSE_ACCEPT:
1134 prompter.get_result (name);
1142 if (name.length()) {
1143 tr->use_new_playlist ();
1144 tr->playlist()->set_name (name);
1149 RouteTimeAxisView::clear_playlist ()
1151 boost::shared_ptr<Track> tr = track ();
1152 if (!tr || tr->destructive()) {
1156 boost::shared_ptr<Playlist> pl = tr->playlist();
1161 _editor.clear_playlist (pl);
1165 RouteTimeAxisView::speed_changed ()
1167 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1171 RouteTimeAxisView::update_diskstream_display ()
1181 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1183 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1185 /* special case: select/deselect all tracks */
1186 if (_editor.get_selection().selected (this)) {
1187 _editor.get_selection().clear_tracks ();
1189 _editor.select_all_tracks ();
1195 switch (ArdourKeyboard::selection_type (ev->state)) {
1196 case Selection::Toggle:
1197 _editor.get_selection().toggle (this);
1200 case Selection::Set:
1201 _editor.get_selection().set (this);
1204 case Selection::Extend:
1205 _editor.extend_selection_to_track (*this);
1208 case Selection::Add:
1209 _editor.get_selection().add (this);
1215 RouteTimeAxisView::set_selected_points (PointSelection& points)
1217 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1218 (*i)->set_selected_points (points);
1223 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1226 _view->set_selected_regionviews (regions);
1230 /** Add the selectable things that we have to a list.
1231 * @param results List to add things to.
1234 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1239 speed = track()->speed();
1242 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1243 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1245 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1246 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1249 /* pick up visible automation tracks */
1251 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1252 if (!(*i)->hidden()) {
1253 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1259 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1262 _view->get_inverted_selectables (sel, results);
1265 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1266 if (!(*i)->hidden()) {
1267 (*i)->get_inverted_selectables (sel, results);
1275 RouteTimeAxisView::route_group () const
1277 return _route->route_group();
1281 RouteTimeAxisView::name() const
1283 return _route->name();
1286 boost::shared_ptr<Playlist>
1287 RouteTimeAxisView::playlist () const
1289 boost::shared_ptr<Track> tr;
1291 if ((tr = track()) != 0) {
1292 return tr->playlist();
1294 return boost::shared_ptr<Playlist> ();
1299 RouteTimeAxisView::name_entry_changed ()
1301 string x = name_entry.get_text ();
1303 if (x == _route->name()) {
1307 strip_whitespace_edges (x);
1309 if (x.length() == 0) {
1310 name_entry.set_text (_route->name());
1314 if (_session->route_name_internal (x)) {
1315 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1317 name_entry.grab_focus ();
1318 } else if (RouteUI::verify_new_route_name (x)) {
1319 _route->set_name (x);
1321 name_entry.grab_focus ();
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 RadioMenuItem::Group playlist_group;
1470 boost::shared_ptr<Track> tr = track ();
1472 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1474 /* sort the playlists */
1476 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1478 /* add the playlists to the menu */
1479 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1480 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1481 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1482 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1484 if (tr->playlist()->id() == (*i)->id()) {
1490 playlist_items.push_back (SeparatorElem());
1491 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1492 playlist_items.push_back (SeparatorElem());
1494 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1495 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1496 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1499 // Use a label which tells the user what is happening
1500 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1501 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1505 playlist_items.push_back (SeparatorElem());
1506 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1507 playlist_items.push_back (SeparatorElem());
1509 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1513 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1515 assert (is_track());
1517 // exit if we were triggered by deactivating the old playlist
1518 if (!item->get_active()) {
1522 boost::shared_ptr<Playlist> pl (wpl.lock());
1528 if (track()->playlist() == pl) {
1529 // exit when use_playlist is called by the creation of the playlist menu
1530 // or the playlist choice is unchanged
1534 track()->use_playlist (pl);
1536 RouteGroup* rg = route_group();
1538 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1539 std::string group_string = "." + rg->name() + ".";
1541 std::string take_name = pl->name();
1542 std::string::size_type idx = take_name.find(group_string);
1544 if (idx == std::string::npos)
1547 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1549 boost::shared_ptr<RouteList> rl (rg->route_list());
1551 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1552 if ( (*i) == this->route()) {
1556 std::string playlist_name = (*i)->name()+group_string+take_name;
1558 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1563 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1565 // No playlist for this track for this take yet, make it
1566 track->use_new_playlist();
1567 track->playlist()->set_name(playlist_name);
1569 track->use_playlist(ipl);
1576 RouteTimeAxisView::show_playlist_selector ()
1578 _editor.playlist_selector().show_for (this);
1582 RouteTimeAxisView::map_frozen ()
1588 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1590 switch (track()->freeze_state()) {
1592 playlist_button.set_sensitive (false);
1593 rec_enable_button->set_sensitive (false);
1596 playlist_button.set_sensitive (true);
1597 rec_enable_button->set_sensitive (true);
1603 RouteTimeAxisView::color_handler ()
1605 //case cTimeStretchOutline:
1606 if (timestretch_rect) {
1607 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1609 //case cTimeStretchFill:
1610 if (timestretch_rect) {
1611 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1617 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1618 * Will add track if necessary.
1621 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1623 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1624 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1627 /* it doesn't exist yet, so we don't care about the button state: just add it */
1628 create_automation_child (param, true);
1631 bool yn = menu->get_active();
1632 bool changed = false;
1634 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1636 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1637 will have done that for us.
1640 if (changed && !no_redraw) {
1648 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1650 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1656 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1658 if (menu && !_hidden) {
1659 ignore_toggle = true;
1660 menu->set_active (false);
1661 ignore_toggle = false;
1664 if (_route && !no_redraw) {
1671 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1673 if (apply_to_selection) {
1674 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1678 /* Show our automation */
1680 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1681 i->second->set_marked_for_display (true);
1683 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1686 menu->set_active(true);
1691 /* Show processor automation */
1693 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1694 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1695 if ((*ii)->view == 0) {
1696 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1699 (*ii)->menu_item->set_active (true);
1712 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1714 if (apply_to_selection) {
1715 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1719 /* Show our automation */
1721 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1722 if (i->second->has_automation()) {
1723 i->second->set_marked_for_display (true);
1725 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1727 menu->set_active(true);
1732 /* Show processor automation */
1734 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1735 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1736 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1737 (*ii)->menu_item->set_active (true);
1749 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1751 if (apply_to_selection) {
1752 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1756 /* Hide our automation */
1758 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1759 i->second->set_marked_for_display (false);
1761 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1764 menu->set_active (false);
1768 /* Hide processor automation */
1770 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1771 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1772 (*ii)->menu_item->set_active (false);
1783 RouteTimeAxisView::region_view_added (RegionView* rv)
1785 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1786 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1787 boost::shared_ptr<AutomationTimeAxisView> atv;
1789 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1794 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1795 (*i)->add_ghost(rv);
1799 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1801 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1807 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1809 parent.remove_processor_automation_node (this);
1813 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1816 remove_child (pan->view);
1820 RouteTimeAxisView::ProcessorAutomationNode*
1821 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1823 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1825 if ((*i)->processor == processor) {
1827 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1828 if ((*ii)->what == what) {
1838 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1840 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1843 ProcessorAutomationNode* pan;
1845 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1846 /* session state may never have been saved with new plugin */
1847 error << _("programming error: ")
1848 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1849 processor->name(), what.type(), (int) what.channel(), what.id() )
1859 boost::shared_ptr<AutomationControl> control
1860 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1862 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1863 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1864 _editor, *this, false, parent_canvas,
1865 processor->describe_parameter (what), processor->name()));
1867 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1869 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1872 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1877 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1880 pan->menu_item->set_active (false);
1889 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1891 boost::shared_ptr<Processor> processor (p.lock ());
1893 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1894 /* The Amp processor is a special case and is dealt with separately */
1898 set<Evoral::Parameter> existing;
1900 processor->what_has_data (existing);
1902 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1904 Evoral::Parameter param (*i);
1905 boost::shared_ptr<AutomationLine> al;
1907 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1910 add_processor_automation_curve (processor, param);
1916 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1918 using namespace Menu_Helpers;
1922 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1924 _automation_tracks[param] = track;
1926 /* existing state overrides "show" argument */
1927 string s = track->gui_property ("visible");
1929 show = string_is_affirmative (s);
1932 /* this might or might not change the visibility status, so don't rely on it */
1933 track->set_marked_for_display (show);
1935 if (show && !no_redraw) {
1939 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1940 /* MIDI-related parameters are always in the menu, there's no
1941 reason to rebuild the menu just because we added a automation
1942 lane for one of them. But if we add a non-MIDI automation
1943 lane, then we need to invalidate the display menu.
1945 delete display_menu;
1951 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1953 boost::shared_ptr<Processor> processor (p.lock ());
1955 if (!processor || !processor->display_to_user ()) {
1959 /* we use this override to veto the Amp processor from the plugin menu,
1960 as its automation lane can be accessed using the special "Fader" menu
1964 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1968 using namespace Menu_Helpers;
1969 ProcessorAutomationInfo *rai;
1970 list<ProcessorAutomationInfo*>::iterator x;
1972 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1974 if (automatable.empty()) {
1978 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1979 if ((*x)->processor == processor) {
1984 if (x == processor_automation.end()) {
1986 rai = new ProcessorAutomationInfo (processor);
1987 processor_automation.push_back (rai);
1995 /* any older menu was deleted at the top of processors_changed()
1996 when we cleared the subplugin menu.
1999 rai->menu = manage (new Menu);
2000 MenuList& items = rai->menu->items();
2001 rai->menu->set_name ("ArdourContextMenu");
2005 std::set<Evoral::Parameter> has_visible_automation;
2006 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2008 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2010 ProcessorAutomationNode* pan;
2011 CheckMenuItem* mitem;
2013 string name = processor->describe_parameter (*i);
2015 items.push_back (CheckMenuElem (name));
2016 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2018 _subplugin_menu_map[*i] = mitem;
2020 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2021 mitem->set_active(true);
2024 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2028 pan = new ProcessorAutomationNode (*i, mitem, *this);
2030 rai->lines.push_back (pan);
2034 pan->menu_item = mitem;
2038 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2041 /* add the menu for this processor, because the subplugin
2042 menu is always cleared at the top of processors_changed().
2043 this is the result of some poor design in gtkmm and/or
2047 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2052 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2053 RouteTimeAxisView::ProcessorAutomationNode* pan)
2055 bool showit = pan->menu_item->get_active();
2056 bool redraw = false;
2058 if (pan->view == 0 && showit) {
2059 add_processor_automation_curve (rai->processor, pan->what);
2063 if (pan->view && pan->view->set_marked_for_display (showit)) {
2067 if (redraw && !no_redraw) {
2073 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2075 if (c.type == RouteProcessorChange::MeterPointChange) {
2076 /* nothing to do if only the meter point has changed */
2080 using namespace Menu_Helpers;
2082 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2083 (*i)->valid = false;
2086 setup_processor_menu_and_curves ();
2088 bool deleted_processor_automation = false;
2090 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2092 list<ProcessorAutomationInfo*>::iterator tmp;
2100 processor_automation.erase (i);
2101 deleted_processor_automation = true;
2108 if (deleted_processor_automation && !no_redraw) {
2113 boost::shared_ptr<AutomationLine>
2114 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2116 ProcessorAutomationNode* pan;
2118 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2124 return boost::shared_ptr<AutomationLine>();
2128 RouteTimeAxisView::reset_processor_automation_curves ()
2130 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2136 RouteTimeAxisView::update_rec_display ()
2138 RouteUI::update_rec_display ();
2139 name_entry.set_sensitive (!_route->record_enabled());
2143 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2145 if (apply_to_selection) {
2146 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2150 _view->set_layer_display (d);
2153 set_gui_property (X_("layer-display"), enum_2_string (d));
2158 RouteTimeAxisView::layer_display () const
2161 return _view->layer_display ();
2164 /* we don't know, since we don't have a _view, so just return something */
2170 boost::shared_ptr<AutomationTimeAxisView>
2171 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2173 AutomationTracks::iterator i = _automation_tracks.find(param);
2174 if (i != _automation_tracks.end()) {
2177 return boost::shared_ptr<AutomationTimeAxisView>();
2182 RouteTimeAxisView::fast_update ()
2184 gm.get_level_meter().update_meters ();
2188 RouteTimeAxisView::hide_meter ()
2191 gm.get_level_meter().hide_meters ();
2195 RouteTimeAxisView::show_meter ()
2201 RouteTimeAxisView::reset_meter ()
2203 if (Config->get_show_track_meters()) {
2204 gm.get_level_meter().setup_meters (height-5);
2211 RouteTimeAxisView::clear_meter ()
2213 gm.get_level_meter().clear_meters ();
2217 RouteTimeAxisView::meter_changed ()
2219 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2224 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2230 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2232 using namespace Menu_Helpers;
2234 if (!_underlay_streams.empty()) {
2235 MenuList& parent_items = parent_menu->items();
2236 Menu* gs_menu = manage (new Menu);
2237 gs_menu->set_name ("ArdourContextMenu");
2238 MenuList& gs_items = gs_menu->items();
2240 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2242 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2243 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2244 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2250 RouteTimeAxisView::set_underlay_state()
2252 if (!underlay_xml_node) {
2256 XMLNodeList nlist = underlay_xml_node->children();
2257 XMLNodeConstIterator niter;
2258 XMLNode *child_node;
2260 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2261 child_node = *niter;
2263 if (child_node->name() != "Underlay") {
2267 XMLProperty* prop = child_node->property ("id");
2269 PBD::ID id (prop->value());
2271 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2274 add_underlay(v->view(), false);
2283 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2289 RouteTimeAxisView& other = v->trackview();
2291 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2292 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2293 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2297 _underlay_streams.push_back(v);
2298 other._underlay_mirrors.push_back(this);
2300 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2302 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2304 if (!underlay_xml_node) {
2305 underlay_xml_node = xml_node->add_child("Underlays");
2308 XMLNode* node = underlay_xml_node->add_child("Underlay");
2309 XMLProperty* prop = node->add_property("id");
2310 prop->set_value(v->trackview().route()->id().to_s());
2317 RouteTimeAxisView::remove_underlay (StreamView* v)
2323 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2324 RouteTimeAxisView& other = v->trackview();
2326 if (it != _underlay_streams.end()) {
2327 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2329 if (gm == other._underlay_mirrors.end()) {
2330 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2334 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2336 _underlay_streams.erase(it);
2337 other._underlay_mirrors.erase(gm);
2339 if (underlay_xml_node) {
2340 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2346 RouteTimeAxisView::set_button_names ()
2348 if (_route && _route->solo_safe()) {
2349 solo_button->remove ();
2350 if (solo_safe_pixbuf == 0) {
2351 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2353 solo_button->set_image (solo_safe_pixbuf);
2354 solo_button->set_text (string());
2356 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2357 if (Config->get_solo_control_is_listen_control()) {
2358 switch (Config->get_listen_position()) {
2359 case AfterFaderListen:
2360 solo_button->set_text (_("A"));
2362 case PreFaderListen:
2363 solo_button->set_text (_("P"));
2367 solo_button->set_text (_("s"));
2370 mute_button->set_text (_("m"));
2374 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2376 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2377 if (i != _main_automation_menu_map.end()) {
2381 i = _subplugin_menu_map.find (param);
2382 if (i != _subplugin_menu_map.end()) {
2390 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2392 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2394 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2398 gain_track.reset (new AutomationTimeAxisView (_session,
2399 _route, _route->amp(), c, param,
2404 _route->amp()->describe_parameter(param)));
2407 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2410 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2414 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2416 l->push_back (rv->region());
2420 RouteTimeAxisView::combine_regions ()
2422 /* as of may 2011, we do not offer uncombine for MIDI tracks
2425 if (!is_audio_track()) {
2433 Playlist::RegionList selected_regions;
2434 boost::shared_ptr<Playlist> playlist = track()->playlist();
2436 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2438 if (selected_regions.size() < 2) {
2442 playlist->clear_changes ();
2443 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2445 _session->add_command (new StatefulDiffCommand (playlist));
2446 /* make the new region be selected */
2448 return _view->find_view (compound_region);
2452 RouteTimeAxisView::uncombine_regions ()
2454 /* as of may 2011, we do not offer uncombine for MIDI tracks
2456 if (!is_audio_track()) {
2464 Playlist::RegionList selected_regions;
2465 boost::shared_ptr<Playlist> playlist = track()->playlist();
2467 /* have to grab selected regions first because the uncombine is going
2468 * to change that in the middle of the list traverse
2471 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2473 playlist->clear_changes ();
2475 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2476 playlist->uncombine (*i);
2479 _session->add_command (new StatefulDiffCommand (playlist));
2483 RouteTimeAxisView::state_id() const
2485 return string_compose ("rtav %1", _route->id().to_s());
2490 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2492 TimeAxisView::remove_child (c);
2494 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2496 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2497 if (i->second == a) {
2498 _automation_tracks.erase (i);