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/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
91 RouteTimeAxisView::setup_slider_pix ()
93 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
94 throw failed_constructor ();
98 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
101 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
103 , parent_canvas (canvas)
105 , button_table (3, 3)
106 , route_group_button (_("g"))
107 , playlist_button (_("p"))
108 , automation_button (_("a"))
109 , automation_action_menu (0)
110 , plugins_submenu_item (0)
111 , route_group_menu (0)
112 , playlist_action_menu (0)
114 , color_mode_menu (0)
115 , gm (sess, slider, true, 115)
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
122 RouteUI::set_route (rt);
124 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
125 gm.get_level_meter().set_no_show_all();
126 gm.get_level_meter().setup_meters(50);
128 string str = gui_property ("height");
130 set_height (atoi (str));
132 set_height (preset_height (HeightNormal));
135 if (!_route->is_hidden()) {
136 if (gui_property ("visible").empty()) {
137 set_gui_property ("visible", true);
140 set_gui_property ("visible", false);
144 update_solo_display ();
146 timestretch_rect = 0;
149 ignore_toggle = false;
151 route_group_button.set_name ("route button");
152 playlist_button.set_name ("route button");
153 automation_button.set_name ("route button");
155 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
156 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
157 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
163 switch (track()->mode()) {
165 case ARDOUR::NonLayered:
166 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
168 case ARDOUR::Destructive:
169 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
173 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
175 if (is_midi_track()) {
176 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
178 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
181 rec_enable_button->set_sensitive (_session->writable());
184 controls_hbox.pack_start(gm.get_level_meter(), false, false);
185 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
186 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
187 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
189 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
191 if (!_route->is_master()) {
192 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
195 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
196 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
198 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
199 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
200 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
201 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
203 if (is_midi_track()) {
204 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
206 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
211 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
213 if (is_track() && track()->mode() == ARDOUR::Normal) {
214 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
220 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
224 str = gui_property ("layer-display");
226 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
229 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
230 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
232 /* pick up the correct freeze state */
236 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
237 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
238 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
240 PropertyList* plist = new PropertyList();
242 plist->add (ARDOUR::Properties::edit, true);
243 plist->add (ARDOUR::Properties::mute, true);
244 plist->add (ARDOUR::Properties::solo, true);
246 route_group_menu = new RouteGroupMenu (_session, plist);
248 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
249 gm.get_gain_slider().set_name ("TrackGainFader");
251 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
257 RouteTimeAxisView::~RouteTimeAxisView ()
259 CatchDeletion (this);
261 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
265 delete playlist_action_menu;
266 playlist_action_menu = 0;
271 _automation_tracks.clear ();
273 delete route_group_menu;
277 RouteTimeAxisView::post_construct ()
279 /* map current state of the route */
281 update_diskstream_display ();
282 setup_processor_menu_and_curves ();
283 reset_processor_automation_curves ();
286 /** Set up the processor menu for the current set of processors, and
287 * display automation curves for any parameters which have data.
290 RouteTimeAxisView::setup_processor_menu_and_curves ()
292 _subplugin_menu_map.clear ();
293 subplugin_menu.items().clear ();
294 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
295 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
299 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
301 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
302 if (_route->route_group()) {
303 _route->route_group()->remove (_route);
309 r.push_back (route ());
311 route_group_menu->build (r);
312 route_group_menu->menu()->popup (ev->button, ev->time);
318 RouteTimeAxisView::playlist_changed ()
324 RouteTimeAxisView::label_view ()
326 string x = _route->name();
328 if (x != name_entry.get_text()) {
329 name_entry.set_text (x);
332 if (x != name_label.get_text()) {
333 name_label.set_text (x);
336 ARDOUR_UI::instance()->set_tip (name_entry, x);
340 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
342 if (what_changed.contains (ARDOUR::Properties::name)) {
348 RouteTimeAxisView::take_name_changed (void *src)
356 RouteTimeAxisView::playlist_click ()
358 build_playlist_menu ();
359 conditionally_add_to_selection ();
360 playlist_action_menu->popup (1, gtk_get_current_event_time());
364 RouteTimeAxisView::automation_click ()
366 conditionally_add_to_selection ();
367 build_automation_action_menu (false);
368 automation_action_menu->popup (1, gtk_get_current_event_time());
372 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
374 using namespace Menu_Helpers;
376 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
377 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
380 detach_menu (subplugin_menu);
382 _main_automation_menu_map.clear ();
383 delete automation_action_menu;
384 automation_action_menu = new Menu;
386 MenuList& items = automation_action_menu->items();
388 automation_action_menu->set_name ("ArdourContextMenu");
390 items.push_back (MenuElem (_("Show All Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
393 items.push_back (MenuElem (_("Show Existing Automation"),
394 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
396 items.push_back (MenuElem (_("Hide All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
399 items.push_back (SeparatorElem ());
401 /* Attach the plugin submenu. It may have previously been used elsewhere,
402 so it was detached above */
404 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
405 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
409 RouteTimeAxisView::build_display_menu ()
411 using namespace Menu_Helpers;
415 TimeAxisView::build_display_menu ();
417 /* now fill it with our stuff */
419 MenuList& items = display_menu->items();
420 display_menu->set_name ("ArdourContextMenu");
422 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
425 detach_menu (*_size_menu);
428 items.push_back (MenuElem (_("Height"), *_size_menu));
430 items.push_back (SeparatorElem());
432 if (!Profile->get_sae()) {
433 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
434 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
435 items.push_back (SeparatorElem());
438 // Hook for derived classes to add type specific stuff
439 append_extra_display_menu_items ();
443 Menu* layers_menu = manage (new Menu);
444 MenuList &layers_items = layers_menu->items();
445 layers_menu->set_name("ArdourContextMenu");
447 RadioMenuItem::Group layers_group;
449 /* Find out how many overlaid/stacked tracks we have in the selection */
453 TrackSelection const & s = _editor.get_selection().tracks;
454 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
455 StreamView* v = (*i)->view ();
460 switch (v->layer_display ()) {
471 /* We're not connecting to signal_toggled() here; in the case where these two items are
472 set to be in the `inconsistent' state, it seems that one or other will end up active
473 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
474 select the active one, no toggled signal is emitted so nothing happens.
477 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
478 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
479 i->set_active (overlaid != 0 && stacked == 0);
480 i->set_inconsistent (overlaid != 0 && stacked != 0);
481 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
483 layers_items.push_back (
484 RadioMenuElem (layers_group, _("Stacked"),
485 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
488 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
489 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
490 i->set_active (overlaid == 0 && stacked != 0);
491 i->set_inconsistent (overlaid != 0 && stacked != 0);
493 items.push_back (MenuElem (_("Layers"), *layers_menu));
495 if (!Profile->get_sae()) {
497 Menu* alignment_menu = manage (new Menu);
498 MenuList& alignment_items = alignment_menu->items();
499 alignment_menu->set_name ("ArdourContextMenu");
501 RadioMenuItem::Group align_group;
503 /* Same verbose hacks as for the layering options above */
509 boost::shared_ptr<Track> first_track;
511 TrackSelection const & s = _editor.get_selection().tracks;
512 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
513 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
514 if (!r || !r->is_track ()) {
519 first_track = r->track();
522 switch (r->track()->alignment_choice()) {
526 switch (r->track()->alignment_style()) {
527 case ExistingMaterial:
535 case UseExistingMaterial:
551 inconsistent = false;
560 if (!inconsistent && first_track) {
562 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
563 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
564 i->set_active (automatic != 0 && existing == 0 && capture == 0);
565 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
567 switch (first_track->alignment_choice()) {
569 switch (first_track->alignment_style()) {
570 case ExistingMaterial:
571 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
574 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
582 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
583 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
584 i->set_active (existing != 0 && capture == 0 && automatic == 0);
585 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
587 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
588 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
589 i->set_active (existing == 0 && capture != 0 && automatic == 0);
590 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
592 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
598 Menu* mode_menu = manage (new Menu);
599 MenuList& mode_items = mode_menu->items ();
600 mode_menu->set_name ("ArdourContextMenu");
602 RadioMenuItem::Group mode_group;
608 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
609 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
610 if (!r || !r->is_track ()) {
614 switch (r->track()->mode()) {
627 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
628 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
629 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
630 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
631 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
633 mode_items.push_back (RadioMenuElem (mode_group, _("Tape 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::Destructive, true));
636 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
637 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
639 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered 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::NonLayered, true));
642 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
643 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
645 items.push_back (MenuElem (_("Mode"), *mode_menu));
648 color_mode_menu = build_color_mode_menu();
649 if (color_mode_menu) {
650 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
653 items.push_back (SeparatorElem());
655 build_playlist_menu ();
656 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
657 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
660 route_group_menu->detach ();
663 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
664 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
666 r.push_back (rtv->route ());
671 r.push_back (route ());
674 route_group_menu->build (r);
675 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
677 build_automation_action_menu (true);
678 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
680 items.push_back (SeparatorElem());
684 TrackSelection const & s = _editor.get_selection().tracks;
685 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
686 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
691 if (r->route()->active()) {
698 items.push_back (CheckMenuElem (_("Active")));
699 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
700 bool click_sets_active = true;
701 if (active > 0 && inactive == 0) {
702 i->set_active (true);
703 click_sets_active = false;
704 } else if (active > 0 && inactive > 0) {
705 i->set_inconsistent (true);
707 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
709 items.push_back (SeparatorElem());
710 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
711 if (!Profile->get_sae()) {
712 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
714 items.push_front (SeparatorElem());
715 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
720 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
722 if (apply_to_selection) {
723 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
728 if (!track()->can_use_mode (mode, needs_bounce)) {
734 cerr << "would bounce this one\n";
739 track()->set_mode (mode);
741 rec_enable_button->remove ();
744 case ARDOUR::NonLayered:
746 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
747 rec_enable_button->set_text (string());
749 case ARDOUR::Destructive:
750 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
751 rec_enable_button->set_text (string());
755 rec_enable_button->show_all ();
760 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
766 TimeAxisView::show_timestretch (start, end);
776 /* check that the time selection was made in our route, or our route group.
777 remember that route_group() == 0 implies the route is *not* in a edit group.
780 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
781 /* this doesn't apply to us */
785 /* ignore it if our edit group is not active */
787 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
792 if (timestretch_rect == 0) {
793 timestretch_rect = new SimpleRect (*canvas_display ());
794 timestretch_rect->property_x1() = 0.0;
795 timestretch_rect->property_y1() = 0.0;
796 timestretch_rect->property_x2() = 0.0;
797 timestretch_rect->property_y2() = 0.0;
798 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
799 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
802 timestretch_rect->show ();
803 timestretch_rect->raise_to_top ();
805 x1 = start / _editor.get_current_zoom();
806 x2 = (end - 1) / _editor.get_current_zoom();
807 y2 = current_height() - 2;
809 timestretch_rect->property_x1() = x1;
810 timestretch_rect->property_y1() = 1.0;
811 timestretch_rect->property_x2() = x2;
812 timestretch_rect->property_y2() = y2;
816 RouteTimeAxisView::hide_timestretch ()
818 TimeAxisView::hide_timestretch ();
820 if (timestretch_rect) {
821 timestretch_rect->hide ();
826 RouteTimeAxisView::show_selection (TimeSelection& ts)
830 /* ignore it if our edit group is not active or if the selection was started
831 in some other track or route group (remember that route_group() == 0 means
832 that the track is not in an route group).
835 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
836 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
842 TimeAxisView::show_selection (ts);
846 RouteTimeAxisView::set_height (uint32_t h)
849 bool height_changed = (height == 0) || (h != height);
850 gm.get_level_meter().setup_meters (gmlen);
852 TimeAxisView::set_height (h);
855 _view->set_height ((double) current_height());
858 if (height >= preset_height (HeightNormal)) {
862 gm.get_gain_slider().show();
864 if (!_route || _route->is_monitor()) {
869 if (rec_enable_button)
870 rec_enable_button->show();
872 route_group_button.show();
873 automation_button.show();
875 if (is_track() && track()->mode() == ARDOUR::Normal) {
876 playlist_button.show();
883 gm.get_gain_slider().hide();
885 if (!_route || _route->is_monitor()) {
890 if (rec_enable_button)
891 rec_enable_button->show();
893 route_group_button.hide ();
894 automation_button.hide ();
896 if (is_track() && track()->mode() == ARDOUR::Normal) {
897 playlist_button.hide ();
902 if (height_changed && !no_redraw) {
903 /* only emit the signal if the height really changed */
909 RouteTimeAxisView::route_color_changed ()
912 _view->apply_color (color(), StreamView::RegionColor);
917 RouteTimeAxisView::reset_samples_per_unit ()
919 set_samples_per_unit (_editor.get_current_zoom());
923 RouteTimeAxisView::horizontal_position_changed ()
926 _view->horizontal_position_changed ();
931 RouteTimeAxisView::set_samples_per_unit (double spu)
936 speed = track()->speed();
940 _view->set_samples_per_unit (spu * speed);
943 TimeAxisView::set_samples_per_unit (spu * speed);
947 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
949 if (!mitem->get_active()) {
950 /* this is one of the two calls made when these radio menu items change status. this one
951 is for the item that became inactive, and we want to ignore it.
956 if (apply_to_selection) {
957 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
960 track()->set_align_choice (choice);
966 RouteTimeAxisView::rename_current_playlist ()
968 ArdourPrompter prompter (true);
971 boost::shared_ptr<Track> tr = track();
972 if (!tr || tr->destructive()) {
976 boost::shared_ptr<Playlist> pl = tr->playlist();
981 prompter.set_title (_("Rename Playlist"));
982 prompter.set_prompt (_("New name for playlist:"));
983 prompter.set_initial_text (pl->name());
984 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
985 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
987 switch (prompter.run ()) {
988 case Gtk::RESPONSE_ACCEPT:
989 prompter.get_result (name);
1001 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1003 std::string ret (basename);
1005 std::string const group_string = "." + route_group()->name() + ".";
1007 // iterate through all playlists
1009 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1010 std::string tmp = (*i)->name();
1012 std::string::size_type idx = tmp.find(group_string);
1013 // find those which belong to this group
1014 if (idx != string::npos) {
1015 tmp = tmp.substr(idx + group_string.length());
1017 // and find the largest current number
1018 int x = atoi(tmp.c_str());
1019 if (x > maxnumber) {
1028 snprintf (buf, sizeof(buf), "%d", maxnumber);
1030 ret = this->name() + "." + route_group()->name () + "." + buf;
1036 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1040 boost::shared_ptr<Track> tr = track ();
1041 if (!tr || tr->destructive()) {
1045 boost::shared_ptr<const Playlist> pl = tr->playlist();
1052 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1053 name = resolve_new_group_playlist_name(name, playlists_before_op);
1056 while (_session->playlists->by_name(name)) {
1057 name = Playlist::bump_name (name, *_session);
1060 // TODO: The prompter "new" button should be de-activated if the user
1061 // specifies a playlist name which already exists in the session.
1065 ArdourPrompter prompter (true);
1067 prompter.set_title (_("New Copy Playlist"));
1068 prompter.set_prompt (_("Name for new playlist:"));
1069 prompter.set_initial_text (name);
1070 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1071 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1072 prompter.show_all ();
1074 switch (prompter.run ()) {
1075 case Gtk::RESPONSE_ACCEPT:
1076 prompter.get_result (name);
1084 if (name.length()) {
1085 tr->use_copy_playlist ();
1086 tr->playlist()->set_name (name);
1091 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1095 boost::shared_ptr<Track> tr = track ();
1096 if (!tr || tr->destructive()) {
1100 boost::shared_ptr<const Playlist> pl = tr->playlist();
1107 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1108 name = resolve_new_group_playlist_name(name,playlists_before_op);
1111 while (_session->playlists->by_name(name)) {
1112 name = Playlist::bump_name (name, *_session);
1118 ArdourPrompter prompter (true);
1120 prompter.set_title (_("New Playlist"));
1121 prompter.set_prompt (_("Name for new playlist:"));
1122 prompter.set_initial_text (name);
1123 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1124 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1126 switch (prompter.run ()) {
1127 case Gtk::RESPONSE_ACCEPT:
1128 prompter.get_result (name);
1136 if (name.length()) {
1137 tr->use_new_playlist ();
1138 tr->playlist()->set_name (name);
1143 RouteTimeAxisView::clear_playlist ()
1145 boost::shared_ptr<Track> tr = track ();
1146 if (!tr || tr->destructive()) {
1150 boost::shared_ptr<Playlist> pl = tr->playlist();
1155 _editor.clear_playlist (pl);
1159 RouteTimeAxisView::speed_changed ()
1161 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1165 RouteTimeAxisView::update_diskstream_display ()
1175 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1177 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1179 /* special case: select/deselect all tracks */
1180 if (_editor.get_selection().selected (this)) {
1181 _editor.get_selection().clear_tracks ();
1183 _editor.select_all_tracks ();
1189 switch (ArdourKeyboard::selection_type (ev->state)) {
1190 case Selection::Toggle:
1191 _editor.get_selection().toggle (this);
1194 case Selection::Set:
1195 _editor.get_selection().set (this);
1198 case Selection::Extend:
1199 _editor.extend_selection_to_track (*this);
1202 case Selection::Add:
1203 _editor.get_selection().add (this);
1209 RouteTimeAxisView::set_selected_points (PointSelection& points)
1211 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1212 (*i)->set_selected_points (points);
1217 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1220 _view->set_selected_regionviews (regions);
1224 /** Add the selectable things that we have to a list.
1225 * @param results List to add things to.
1228 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1233 speed = track()->speed();
1236 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1237 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1239 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1240 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1243 /* pick up visible automation tracks */
1245 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1246 if (!(*i)->hidden()) {
1247 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1253 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1256 _view->get_inverted_selectables (sel, results);
1259 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1260 if (!(*i)->hidden()) {
1261 (*i)->get_inverted_selectables (sel, results);
1269 RouteTimeAxisView::route_group () const
1271 return _route->route_group();
1275 RouteTimeAxisView::name() const
1277 return _route->name();
1280 boost::shared_ptr<Playlist>
1281 RouteTimeAxisView::playlist () const
1283 boost::shared_ptr<Track> tr;
1285 if ((tr = track()) != 0) {
1286 return tr->playlist();
1288 return boost::shared_ptr<Playlist> ();
1293 RouteTimeAxisView::name_entry_changed ()
1295 string x = name_entry.get_text ();
1297 if (x == _route->name()) {
1301 strip_whitespace_edges (x);
1303 if (x.length() == 0) {
1304 name_entry.set_text (_route->name());
1308 if (_session->route_name_internal (x)) {
1309 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1311 name_entry.grab_focus ();
1312 } else if (RouteUI::verify_new_route_name (x)) {
1313 _route->set_name (x);
1315 name_entry.grab_focus ();
1319 boost::shared_ptr<Region>
1320 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1322 boost::shared_ptr<Playlist> pl = playlist ();
1325 return pl->find_next_region (pos, point, dir);
1328 return boost::shared_ptr<Region> ();
1332 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1334 boost::shared_ptr<Playlist> pl = playlist ();
1337 return pl->find_next_region_boundary (pos, dir);
1344 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1346 boost::shared_ptr<Playlist> what_we_got;
1347 boost::shared_ptr<Track> tr = track ();
1348 boost::shared_ptr<Playlist> playlist;
1351 /* route is a bus, not a track */
1355 playlist = tr->playlist();
1357 TimeSelection time (selection.time);
1358 float const speed = tr->speed();
1359 if (speed != 1.0f) {
1360 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1361 (*i).start = session_frame_to_track_frame((*i).start, speed);
1362 (*i).end = session_frame_to_track_frame((*i).end, speed);
1366 playlist->clear_changes ();
1367 playlist->clear_owned_changes ();
1371 if (playlist->cut (time) != 0) {
1372 vector<Command*> cmds;
1373 playlist->rdiff (cmds);
1374 _session->add_commands (cmds);
1376 _session->add_command (new StatefulDiffCommand (playlist));
1381 if ((what_we_got = playlist->cut (time)) != 0) {
1382 _editor.get_cut_buffer().add (what_we_got);
1383 vector<Command*> cmds;
1384 playlist->rdiff (cmds);
1385 _session->add_commands (cmds);
1387 _session->add_command (new StatefulDiffCommand (playlist));
1391 if ((what_we_got = playlist->copy (time)) != 0) {
1392 _editor.get_cut_buffer().add (what_we_got);
1397 if ((what_we_got = playlist->cut (time)) != 0) {
1399 vector<Command*> cmds;
1400 playlist->rdiff (cmds);
1401 _session->add_commands (cmds);
1402 _session->add_command (new StatefulDiffCommand (playlist));
1403 what_we_got->release ();
1410 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1416 boost::shared_ptr<Playlist> pl = playlist ();
1417 PlaylistSelection::iterator p;
1419 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1421 if (p == selection.playlists.end()) {
1425 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1427 if (track()->speed() != 1.0f) {
1428 pos = session_frame_to_track_frame (pos, track()->speed());
1429 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1432 pl->clear_changes ();
1433 pl->paste (*p, pos, times);
1434 _session->add_command (new StatefulDiffCommand (pl));
1440 struct PlaylistSorter {
1441 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1442 return a->sort_id() < b->sort_id();
1447 RouteTimeAxisView::build_playlist_menu ()
1449 using namespace Menu_Helpers;
1455 delete playlist_action_menu;
1456 playlist_action_menu = new Menu;
1457 playlist_action_menu->set_name ("ArdourContextMenu");
1459 MenuList& playlist_items = playlist_action_menu->items();
1460 playlist_action_menu->set_name ("ArdourContextMenu");
1461 playlist_items.clear();
1463 RadioMenuItem::Group playlist_group;
1464 boost::shared_ptr<Track> tr = track ();
1466 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1468 /* sort the playlists */
1470 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1472 /* add the playlists to the menu */
1473 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1474 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1475 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1476 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1478 if (tr->playlist()->id() == (*i)->id()) {
1484 playlist_items.push_back (SeparatorElem());
1485 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1486 playlist_items.push_back (SeparatorElem());
1488 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1489 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1490 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1493 // Use a label which tells the user what is happening
1494 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1495 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1499 playlist_items.push_back (SeparatorElem());
1500 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1501 playlist_items.push_back (SeparatorElem());
1503 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1507 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1509 assert (is_track());
1511 // exit if we were triggered by deactivating the old playlist
1512 if (!item->get_active()) {
1516 boost::shared_ptr<Playlist> pl (wpl.lock());
1522 if (track()->playlist() == pl) {
1523 // exit when use_playlist is called by the creation of the playlist menu
1524 // or the playlist choice is unchanged
1528 track()->use_playlist (pl);
1530 RouteGroup* rg = route_group();
1532 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1533 std::string group_string = "." + rg->name() + ".";
1535 std::string take_name = pl->name();
1536 std::string::size_type idx = take_name.find(group_string);
1538 if (idx == std::string::npos)
1541 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1543 boost::shared_ptr<RouteList> rl (rg->route_list());
1545 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1546 if ( (*i) == this->route()) {
1550 std::string playlist_name = (*i)->name()+group_string+take_name;
1552 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1557 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1559 // No playlist for this track for this take yet, make it
1560 track->use_new_playlist();
1561 track->playlist()->set_name(playlist_name);
1563 track->use_playlist(ipl);
1570 RouteTimeAxisView::show_playlist_selector ()
1572 _editor.playlist_selector().show_for (this);
1576 RouteTimeAxisView::map_frozen ()
1582 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1584 switch (track()->freeze_state()) {
1586 playlist_button.set_sensitive (false);
1587 rec_enable_button->set_sensitive (false);
1590 playlist_button.set_sensitive (true);
1591 rec_enable_button->set_sensitive (true);
1597 RouteTimeAxisView::color_handler ()
1599 //case cTimeStretchOutline:
1600 if (timestretch_rect) {
1601 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1603 //case cTimeStretchFill:
1604 if (timestretch_rect) {
1605 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1611 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1612 * Will add track if necessary.
1615 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1617 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1618 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1621 /* it doesn't exist yet, so we don't care about the button state: just add it */
1622 create_automation_child (param, true);
1625 bool yn = menu->get_active();
1626 bool changed = false;
1628 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1630 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1631 will have done that for us.
1634 if (changed && !no_redraw) {
1642 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1644 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1650 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1652 if (menu && !_hidden) {
1653 ignore_toggle = true;
1654 menu->set_active (false);
1655 ignore_toggle = false;
1658 if (_route && !no_redraw) {
1665 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1667 if (apply_to_selection) {
1668 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1672 /* Show our automation */
1674 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1675 i->second->set_marked_for_display (true);
1677 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1680 menu->set_active(true);
1685 /* Show processor automation */
1687 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1688 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1689 if ((*ii)->view == 0) {
1690 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1693 (*ii)->menu_item->set_active (true);
1706 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1708 if (apply_to_selection) {
1709 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1713 /* Show our automation */
1715 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1716 if (i->second->has_automation()) {
1717 i->second->set_marked_for_display (true);
1719 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1721 menu->set_active(true);
1726 /* Show processor automation */
1728 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1729 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1730 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1731 (*ii)->menu_item->set_active (true);
1743 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1745 if (apply_to_selection) {
1746 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1750 /* Hide our automation */
1752 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1753 i->second->set_marked_for_display (false);
1755 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1758 menu->set_active (false);
1762 /* Hide processor automation */
1764 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1765 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1766 (*ii)->menu_item->set_active (false);
1777 RouteTimeAxisView::region_view_added (RegionView* rv)
1779 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1780 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1781 boost::shared_ptr<AutomationTimeAxisView> atv;
1783 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1788 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1789 (*i)->add_ghost(rv);
1793 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1795 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1801 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1803 parent.remove_processor_automation_node (this);
1807 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1810 remove_child (pan->view);
1814 RouteTimeAxisView::ProcessorAutomationNode*
1815 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1817 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1819 if ((*i)->processor == processor) {
1821 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1822 if ((*ii)->what == what) {
1832 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1834 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1837 ProcessorAutomationNode* pan;
1839 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1840 /* session state may never have been saved with new plugin */
1841 error << _("programming error: ")
1842 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1843 processor->name(), what.type(), (int) what.channel(), what.id() )
1853 boost::shared_ptr<AutomationControl> control
1854 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1856 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1857 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1858 _editor, *this, false, parent_canvas,
1859 processor->describe_parameter (what), processor->name()));
1861 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1863 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1866 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1871 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1874 pan->menu_item->set_active (false);
1883 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1885 boost::shared_ptr<Processor> processor (p.lock ());
1887 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1888 /* The Amp processor is a special case and is dealt with separately */
1892 set<Evoral::Parameter> existing;
1894 processor->what_has_data (existing);
1896 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1898 Evoral::Parameter param (*i);
1899 boost::shared_ptr<AutomationLine> al;
1901 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1904 add_processor_automation_curve (processor, param);
1910 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1912 using namespace Menu_Helpers;
1916 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1918 _automation_tracks[param] = track;
1920 /* existing state overrides "show" argument */
1921 string s = track->gui_property ("visible");
1923 show = string_is_affirmative (s);
1926 /* this might or might not change the visibility status, so don't rely on it */
1927 track->set_marked_for_display (show);
1929 if (show && !no_redraw) {
1933 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1934 /* MIDI-related parameters are always in the menu, there's no
1935 reason to rebuild the menu just because we added a automation
1936 lane for one of them. But if we add a non-MIDI automation
1937 lane, then we need to invalidate the display menu.
1939 delete display_menu;
1945 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1947 boost::shared_ptr<Processor> processor (p.lock ());
1949 if (!processor || !processor->display_to_user ()) {
1953 /* we use this override to veto the Amp processor from the plugin menu,
1954 as its automation lane can be accessed using the special "Fader" menu
1958 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1962 using namespace Menu_Helpers;
1963 ProcessorAutomationInfo *rai;
1964 list<ProcessorAutomationInfo*>::iterator x;
1966 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1968 if (automatable.empty()) {
1972 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1973 if ((*x)->processor == processor) {
1978 if (x == processor_automation.end()) {
1980 rai = new ProcessorAutomationInfo (processor);
1981 processor_automation.push_back (rai);
1989 /* any older menu was deleted at the top of processors_changed()
1990 when we cleared the subplugin menu.
1993 rai->menu = manage (new Menu);
1994 MenuList& items = rai->menu->items();
1995 rai->menu->set_name ("ArdourContextMenu");
1999 std::set<Evoral::Parameter> has_visible_automation;
2000 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2002 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2004 ProcessorAutomationNode* pan;
2005 CheckMenuItem* mitem;
2007 string name = processor->describe_parameter (*i);
2009 items.push_back (CheckMenuElem (name));
2010 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2012 _subplugin_menu_map[*i] = mitem;
2014 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2015 mitem->set_active(true);
2018 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2022 pan = new ProcessorAutomationNode (*i, mitem, *this);
2024 rai->lines.push_back (pan);
2028 pan->menu_item = mitem;
2032 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2035 /* add the menu for this processor, because the subplugin
2036 menu is always cleared at the top of processors_changed().
2037 this is the result of some poor design in gtkmm and/or
2041 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2046 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2047 RouteTimeAxisView::ProcessorAutomationNode* pan)
2049 bool showit = pan->menu_item->get_active();
2050 bool redraw = false;
2052 if (pan->view == 0 && showit) {
2053 add_processor_automation_curve (rai->processor, pan->what);
2057 if (pan->view && pan->view->set_marked_for_display (showit)) {
2061 if (redraw && !no_redraw) {
2067 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2069 if (c.type == RouteProcessorChange::MeterPointChange) {
2070 /* nothing to do if only the meter point has changed */
2074 using namespace Menu_Helpers;
2076 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2077 (*i)->valid = false;
2080 setup_processor_menu_and_curves ();
2082 bool deleted_processor_automation = false;
2084 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2086 list<ProcessorAutomationInfo*>::iterator tmp;
2094 processor_automation.erase (i);
2095 deleted_processor_automation = true;
2102 if (deleted_processor_automation && !no_redraw) {
2107 boost::shared_ptr<AutomationLine>
2108 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2110 ProcessorAutomationNode* pan;
2112 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2118 return boost::shared_ptr<AutomationLine>();
2122 RouteTimeAxisView::reset_processor_automation_curves ()
2124 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2130 RouteTimeAxisView::update_rec_display ()
2132 RouteUI::update_rec_display ();
2133 name_entry.set_sensitive (!_route->record_enabled());
2137 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2139 if (apply_to_selection) {
2140 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2144 _view->set_layer_display (d);
2147 set_gui_property (X_("layer-display"), enum_2_string (d));
2152 RouteTimeAxisView::layer_display () const
2155 return _view->layer_display ();
2158 /* we don't know, since we don't have a _view, so just return something */
2164 boost::shared_ptr<AutomationTimeAxisView>
2165 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2167 AutomationTracks::iterator i = _automation_tracks.find(param);
2168 if (i != _automation_tracks.end()) {
2171 return boost::shared_ptr<AutomationTimeAxisView>();
2176 RouteTimeAxisView::fast_update ()
2178 gm.get_level_meter().update_meters ();
2182 RouteTimeAxisView::hide_meter ()
2185 gm.get_level_meter().hide_meters ();
2189 RouteTimeAxisView::show_meter ()
2195 RouteTimeAxisView::reset_meter ()
2197 if (Config->get_show_track_meters()) {
2198 gm.get_level_meter().setup_meters (height-5);
2205 RouteTimeAxisView::clear_meter ()
2207 gm.get_level_meter().clear_meters ();
2211 RouteTimeAxisView::meter_changed ()
2213 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2218 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2224 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2226 using namespace Menu_Helpers;
2228 if (!_underlay_streams.empty()) {
2229 MenuList& parent_items = parent_menu->items();
2230 Menu* gs_menu = manage (new Menu);
2231 gs_menu->set_name ("ArdourContextMenu");
2232 MenuList& gs_items = gs_menu->items();
2234 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2236 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2237 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2238 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2244 RouteTimeAxisView::set_underlay_state()
2246 if (!underlay_xml_node) {
2250 XMLNodeList nlist = underlay_xml_node->children();
2251 XMLNodeConstIterator niter;
2252 XMLNode *child_node;
2254 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2255 child_node = *niter;
2257 if (child_node->name() != "Underlay") {
2261 XMLProperty* prop = child_node->property ("id");
2263 PBD::ID id (prop->value());
2265 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2268 add_underlay(v->view(), false);
2277 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2283 RouteTimeAxisView& other = v->trackview();
2285 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2286 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2287 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2291 _underlay_streams.push_back(v);
2292 other._underlay_mirrors.push_back(this);
2294 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2296 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2298 if (!underlay_xml_node) {
2299 underlay_xml_node = xml_node->add_child("Underlays");
2302 XMLNode* node = underlay_xml_node->add_child("Underlay");
2303 XMLProperty* prop = node->add_property("id");
2304 prop->set_value(v->trackview().route()->id().to_s());
2311 RouteTimeAxisView::remove_underlay (StreamView* v)
2317 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2318 RouteTimeAxisView& other = v->trackview();
2320 if (it != _underlay_streams.end()) {
2321 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2323 if (gm == other._underlay_mirrors.end()) {
2324 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2328 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2330 _underlay_streams.erase(it);
2331 other._underlay_mirrors.erase(gm);
2333 if (underlay_xml_node) {
2334 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2340 RouteTimeAxisView::set_button_names ()
2342 if (_route && _route->solo_safe()) {
2343 solo_button->remove ();
2344 if (solo_safe_pixbuf == 0) {
2345 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2347 solo_button->set_image (solo_safe_pixbuf);
2348 solo_button->set_text (string());
2350 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2351 if (Config->get_solo_control_is_listen_control()) {
2352 switch (Config->get_listen_position()) {
2353 case AfterFaderListen:
2354 solo_button->set_text (_("A"));
2355 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2357 case PreFaderListen:
2358 solo_button->set_text (_("P"));
2359 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2363 solo_button->set_text (_("s"));
2364 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2367 mute_button->set_text (_("m"));
2371 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2373 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2374 if (i != _main_automation_menu_map.end()) {
2378 i = _subplugin_menu_map.find (param);
2379 if (i != _subplugin_menu_map.end()) {
2387 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2389 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2391 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2395 gain_track.reset (new AutomationTimeAxisView (_session,
2396 _route, _route->amp(), c, param,
2401 _route->amp()->describe_parameter(param)));
2404 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2407 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2411 void add_region_to_list (RegionView* rv, RegionList* l)
2413 l->push_back (rv->region());
2417 RouteTimeAxisView::combine_regions ()
2419 /* as of may 2011, we do not offer uncombine for MIDI tracks
2422 if (!is_audio_track()) {
2430 RegionList selected_regions;
2431 boost::shared_ptr<Playlist> playlist = track()->playlist();
2433 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2435 if (selected_regions.size() < 2) {
2439 playlist->clear_changes ();
2440 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2442 _session->add_command (new StatefulDiffCommand (playlist));
2443 /* make the new region be selected */
2445 return _view->find_view (compound_region);
2449 RouteTimeAxisView::uncombine_regions ()
2451 /* as of may 2011, we do not offer uncombine for MIDI tracks
2453 if (!is_audio_track()) {
2461 RegionList selected_regions;
2462 boost::shared_ptr<Playlist> playlist = track()->playlist();
2464 /* have to grab selected regions first because the uncombine is going
2465 * to change that in the middle of the list traverse
2468 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2470 playlist->clear_changes ();
2472 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2473 playlist->uncombine (*i);
2476 _session->add_command (new StatefulDiffCommand (playlist));
2480 RouteTimeAxisView::state_id() const
2482 return string_compose ("rtav %1", _route->id().to_s());
2487 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2489 TimeAxisView::remove_child (c);
2491 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2493 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2494 if (i->second == a) {
2495 _automation_tracks.erase (i);