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;
89 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider_desensitised;
92 RouteTimeAxisView::setup_slider_pix ()
94 if ((slider = ::get_icon ("fader_belt_h_medium")) == 0) {
95 throw failed_constructor ();
98 if ((slider_desensitised = ::get_icon ("fader_belt_h_medium_desensitised")) == 0) {
99 throw failed_constructor ();
103 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
106 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
108 , parent_canvas (canvas)
110 , button_table (3, 3)
111 , route_group_button (_("g"))
112 , playlist_button (_("p"))
113 , automation_button (_("a"))
114 , automation_action_menu (0)
115 , plugins_submenu_item (0)
116 , route_group_menu (0)
117 , playlist_action_menu (0)
119 , color_mode_menu (0)
120 , gm (sess, slider, slider_desensitised, true, 115)
121 , _ignore_set_layer_display (false)
126 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
128 RouteUI::set_route (rt);
130 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
131 gm.get_level_meter().set_no_show_all();
132 gm.get_level_meter().setup_meters(50);
133 gm.update_gain_sensitive ();
135 string str = gui_property ("height");
137 set_height (atoi (str));
139 set_height (preset_height (HeightNormal));
142 if (!_route->is_hidden()) {
143 if (gui_property ("visible").empty()) {
144 set_gui_property ("visible", true);
147 set_gui_property ("visible", false);
151 update_solo_display ();
153 timestretch_rect = 0;
156 ignore_toggle = false;
158 route_group_button.set_name ("route button");
159 playlist_button.set_name ("route button");
160 automation_button.set_name ("route button");
162 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
163 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
164 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
170 switch (track()->mode()) {
172 case ARDOUR::NonLayered:
173 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
175 case ARDOUR::Destructive:
176 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
180 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
182 if (is_midi_track()) {
183 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 rec_enable_button->set_sensitive (_session->writable());
190 /* set playlist button tip to the current playlist, and make it update when it changes */
191 update_playlist_tip ();
192 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
196 controls_hbox.pack_start(gm.get_level_meter(), false, false);
197 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
198 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
199 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
201 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
203 if (!_route->is_master()) {
204 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
207 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
208 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
210 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
211 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
212 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
214 if (is_midi_track()) {
215 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
217 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
222 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
224 if (is_track() && track()->mode() == ARDOUR::Normal) {
225 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
230 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
231 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
235 str = gui_property ("layer-display");
237 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
240 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
241 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
243 /* pick up the correct freeze state */
247 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
248 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
249 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
251 PropertyList* plist = new PropertyList();
253 plist->add (ARDOUR::Properties::edit, true);
254 plist->add (ARDOUR::Properties::mute, true);
255 plist->add (ARDOUR::Properties::solo, true);
257 route_group_menu = new RouteGroupMenu (_session, plist);
259 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
260 gm.get_gain_slider().set_name ("TrackGainFader");
262 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
268 RouteTimeAxisView::~RouteTimeAxisView ()
270 CatchDeletion (this);
272 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
276 delete playlist_action_menu;
277 playlist_action_menu = 0;
282 _automation_tracks.clear ();
284 delete route_group_menu;
288 RouteTimeAxisView::post_construct ()
290 /* map current state of the route */
292 update_diskstream_display ();
293 setup_processor_menu_and_curves ();
294 reset_processor_automation_curves ();
297 /** Set up the processor menu for the current set of processors, and
298 * display automation curves for any parameters which have data.
301 RouteTimeAxisView::setup_processor_menu_and_curves ()
303 _subplugin_menu_map.clear ();
304 subplugin_menu.items().clear ();
305 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
306 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
310 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
312 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
313 if (_route->route_group()) {
314 _route->route_group()->remove (_route);
320 r.push_back (route ());
322 route_group_menu->build (r);
323 route_group_menu->menu()->popup (ev->button, ev->time);
329 RouteTimeAxisView::playlist_changed ()
335 RouteTimeAxisView::label_view ()
337 string x = _route->name();
339 if (x != name_entry.get_text()) {
340 name_entry.set_text (x);
343 if (x != name_label.get_text()) {
344 name_label.set_text (x);
347 ARDOUR_UI::instance()->set_tip (name_entry, x);
351 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
353 if (what_changed.contains (ARDOUR::Properties::name)) {
359 RouteTimeAxisView::take_name_changed (void *src)
367 RouteTimeAxisView::playlist_click ()
369 build_playlist_menu ();
370 conditionally_add_to_selection ();
371 playlist_action_menu->popup (1, gtk_get_current_event_time());
375 RouteTimeAxisView::automation_click ()
377 conditionally_add_to_selection ();
378 build_automation_action_menu (false);
379 automation_action_menu->popup (1, gtk_get_current_event_time());
383 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
385 using namespace Menu_Helpers;
387 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
388 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
391 detach_menu (subplugin_menu);
393 _main_automation_menu_map.clear ();
394 delete automation_action_menu;
395 automation_action_menu = new Menu;
397 MenuList& items = automation_action_menu->items();
399 automation_action_menu->set_name ("ArdourContextMenu");
401 items.push_back (MenuElem (_("Show All Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
404 items.push_back (MenuElem (_("Show Existing Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
407 items.push_back (MenuElem (_("Hide All Automation"),
408 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
410 items.push_back (SeparatorElem ());
412 /* Attach the plugin submenu. It may have previously been used elsewhere,
413 so it was detached above */
415 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
416 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
420 RouteTimeAxisView::build_display_menu ()
422 using namespace Menu_Helpers;
426 TimeAxisView::build_display_menu ();
428 /* now fill it with our stuff */
430 MenuList& items = display_menu->items();
431 display_menu->set_name ("ArdourContextMenu");
433 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
436 detach_menu (*_size_menu);
439 items.push_back (MenuElem (_("Height"), *_size_menu));
441 items.push_back (SeparatorElem());
443 if (!Profile->get_sae()) {
444 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
445 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
446 items.push_back (SeparatorElem());
449 // Hook for derived classes to add type specific stuff
450 append_extra_display_menu_items ();
454 Menu* layers_menu = manage (new Menu);
455 MenuList &layers_items = layers_menu->items();
456 layers_menu->set_name("ArdourContextMenu");
458 RadioMenuItem::Group layers_group;
460 /* Find out how many overlaid/stacked tracks we have in the selection */
464 TrackSelection const & s = _editor.get_selection().tracks;
465 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
466 StreamView* v = (*i)->view ();
471 switch (v->layer_display ()) {
482 /* We're not connecting to signal_toggled() here; in the case where these two items are
483 set to be in the `inconsistent' state, it seems that one or other will end up active
484 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
485 select the active one, no toggled signal is emitted so nothing happens.
488 _ignore_set_layer_display = true;
490 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
491 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
492 i->set_active (overlaid != 0 && stacked == 0);
493 i->set_inconsistent (overlaid != 0 && stacked != 0);
494 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
496 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
497 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
498 i->set_active (overlaid == 0 && stacked != 0);
499 i->set_inconsistent (overlaid != 0 && stacked != 0);
500 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
502 _ignore_set_layer_display = false;
504 items.push_back (MenuElem (_("Layers"), *layers_menu));
506 if (!Profile->get_sae()) {
508 Menu* alignment_menu = manage (new Menu);
509 MenuList& alignment_items = alignment_menu->items();
510 alignment_menu->set_name ("ArdourContextMenu");
512 RadioMenuItem::Group align_group;
514 /* Same verbose hacks as for the layering options above */
520 boost::shared_ptr<Track> first_track;
522 TrackSelection const & s = _editor.get_selection().tracks;
523 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
524 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
525 if (!r || !r->is_track ()) {
530 first_track = r->track();
533 switch (r->track()->alignment_choice()) {
537 switch (r->track()->alignment_style()) {
538 case ExistingMaterial:
546 case UseExistingMaterial:
562 inconsistent = false;
571 if (!inconsistent && first_track) {
573 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
574 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
575 i->set_active (automatic != 0 && existing == 0 && capture == 0);
576 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
578 switch (first_track->alignment_choice()) {
580 switch (first_track->alignment_style()) {
581 case ExistingMaterial:
582 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
585 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing != 0 && capture == 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
598 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
599 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
600 i->set_active (existing == 0 && capture != 0 && automatic == 0);
601 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
603 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
609 Menu* mode_menu = manage (new Menu);
610 MenuList& mode_items = mode_menu->items ();
611 mode_menu->set_name ("ArdourContextMenu");
613 RadioMenuItem::Group mode_group;
619 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
620 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
621 if (!r || !r->is_track ()) {
625 switch (r->track()->mode()) {
638 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
641 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
642 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
644 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
645 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
647 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
648 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
650 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
651 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
652 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
653 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
654 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
656 items.push_back (MenuElem (_("Mode"), *mode_menu));
659 color_mode_menu = build_color_mode_menu();
660 if (color_mode_menu) {
661 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
664 items.push_back (SeparatorElem());
666 build_playlist_menu ();
667 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
668 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
671 route_group_menu->detach ();
674 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
675 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
677 r.push_back (rtv->route ());
682 r.push_back (route ());
685 route_group_menu->build (r);
686 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
688 build_automation_action_menu (true);
689 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
691 items.push_back (SeparatorElem());
695 TrackSelection const & s = _editor.get_selection().tracks;
696 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
697 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
702 if (r->route()->active()) {
709 items.push_back (CheckMenuElem (_("Active")));
710 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
711 bool click_sets_active = true;
712 if (active > 0 && inactive == 0) {
713 i->set_active (true);
714 click_sets_active = false;
715 } else if (active > 0 && inactive > 0) {
716 i->set_inconsistent (true);
718 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
720 items.push_back (SeparatorElem());
721 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
722 if (!Profile->get_sae()) {
723 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
725 items.push_front (SeparatorElem());
726 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
731 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
733 if (apply_to_selection) {
734 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
739 if (!track()->can_use_mode (mode, needs_bounce)) {
745 cerr << "would bounce this one\n";
750 track()->set_mode (mode);
752 rec_enable_button->remove ();
755 case ARDOUR::NonLayered:
757 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
758 rec_enable_button->set_text (string());
760 case ARDOUR::Destructive:
761 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
762 rec_enable_button->set_text (string());
766 rec_enable_button->show_all ();
771 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
773 TimeAxisView::show_timestretch (start, end, layers, layer);
783 /* check that the time selection was made in our route, or our route group.
784 remember that route_group() == 0 implies the route is *not* in a edit group.
787 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
788 /* this doesn't apply to us */
792 /* ignore it if our edit group is not active */
794 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
799 if (timestretch_rect == 0) {
800 timestretch_rect = new SimpleRect (*canvas_display ());
801 timestretch_rect->property_x1() = 0.0;
802 timestretch_rect->property_y1() = 0.0;
803 timestretch_rect->property_x2() = 0.0;
804 timestretch_rect->property_y2() = 0.0;
805 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
806 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
809 timestretch_rect->show ();
810 timestretch_rect->raise_to_top ();
812 double const x1 = start / _editor.get_current_zoom();
813 double const x2 = (end - 1) / _editor.get_current_zoom();
815 timestretch_rect->property_x1() = x1;
816 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
817 timestretch_rect->property_x2() = x2;
818 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
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 if (track->freeze_state() == Track::Frozen) {
1564 /* Don't change playlists of frozen tracks */
1568 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1570 // No playlist for this track for this take yet, make it
1571 track->use_new_playlist();
1572 track->playlist()->set_name(playlist_name);
1574 track->use_playlist(ipl);
1581 RouteTimeAxisView::update_playlist_tip ()
1583 RouteGroup* rg = route_group ();
1584 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1585 string group_string = "." + rg->name() + ".";
1587 string take_name = track()->playlist()->name();
1588 string::size_type idx = take_name.find(group_string);
1590 if (idx != string::npos) {
1591 /* find the bit containing the take number / name */
1592 take_name = take_name.substr (idx + group_string.length());
1594 /* set the playlist button tooltip to the take name */
1595 ARDOUR_UI::instance()->set_tip (
1597 string_compose(_("Take: %1.%2"), escape_angled_brackets (rg->name()), escape_angled_brackets (take_name))
1604 /* set the playlist button tooltip to the playlist name */
1605 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + escape_angled_brackets (track()->playlist()->name()));
1610 RouteTimeAxisView::show_playlist_selector ()
1612 _editor.playlist_selector().show_for (this);
1616 RouteTimeAxisView::map_frozen ()
1622 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1624 switch (track()->freeze_state()) {
1626 playlist_button.set_sensitive (false);
1627 rec_enable_button->set_sensitive (false);
1630 playlist_button.set_sensitive (true);
1631 rec_enable_button->set_sensitive (true);
1637 RouteTimeAxisView::color_handler ()
1639 //case cTimeStretchOutline:
1640 if (timestretch_rect) {
1641 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1643 //case cTimeStretchFill:
1644 if (timestretch_rect) {
1645 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1651 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1652 * Will add track if necessary.
1655 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1657 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1658 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1661 /* it doesn't exist yet, so we don't care about the button state: just add it */
1662 create_automation_child (param, true);
1665 bool yn = menu->get_active();
1666 bool changed = false;
1668 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1670 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1671 will have done that for us.
1674 if (changed && !no_redraw) {
1682 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1684 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1690 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1692 if (menu && !_hidden) {
1693 ignore_toggle = true;
1694 menu->set_active (false);
1695 ignore_toggle = false;
1698 if (_route && !no_redraw) {
1705 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1707 if (apply_to_selection) {
1708 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1712 /* Show our automation */
1714 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1715 i->second->set_marked_for_display (true);
1717 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1720 menu->set_active(true);
1725 /* Show processor automation */
1727 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1728 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1729 if ((*ii)->view == 0) {
1730 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1733 (*ii)->menu_item->set_active (true);
1746 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1748 if (apply_to_selection) {
1749 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1753 /* Show our automation */
1755 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1756 if (i->second->has_automation()) {
1757 i->second->set_marked_for_display (true);
1759 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1761 menu->set_active(true);
1766 /* Show processor automation */
1768 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1769 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1770 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1771 (*ii)->menu_item->set_active (true);
1783 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1785 if (apply_to_selection) {
1786 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1790 /* Hide our automation */
1792 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1793 i->second->set_marked_for_display (false);
1795 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1798 menu->set_active (false);
1802 /* Hide processor automation */
1804 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1805 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1806 (*ii)->menu_item->set_active (false);
1817 RouteTimeAxisView::region_view_added (RegionView* rv)
1819 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1820 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1821 boost::shared_ptr<AutomationTimeAxisView> atv;
1823 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1828 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1829 (*i)->add_ghost(rv);
1833 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1835 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1841 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1843 parent.remove_processor_automation_node (this);
1847 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1850 remove_child (pan->view);
1854 RouteTimeAxisView::ProcessorAutomationNode*
1855 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1857 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1859 if ((*i)->processor == processor) {
1861 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1862 if ((*ii)->what == what) {
1872 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1874 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1877 ProcessorAutomationNode* pan;
1879 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1880 /* session state may never have been saved with new plugin */
1881 error << _("programming error: ")
1882 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1883 processor->name(), what.type(), (int) what.channel(), what.id() )
1893 boost::shared_ptr<AutomationControl> control
1894 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1896 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1897 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1898 _editor, *this, false, parent_canvas,
1899 processor->describe_parameter (what), processor->name()));
1901 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1903 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1906 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1911 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1914 pan->menu_item->set_active (false);
1923 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1925 boost::shared_ptr<Processor> processor (p.lock ());
1927 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1928 /* The Amp processor is a special case and is dealt with separately */
1932 set<Evoral::Parameter> existing;
1934 processor->what_has_data (existing);
1936 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1938 Evoral::Parameter param (*i);
1939 boost::shared_ptr<AutomationLine> al;
1941 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1944 add_processor_automation_curve (processor, param);
1950 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1952 using namespace Menu_Helpers;
1956 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1958 _automation_tracks[param] = track;
1960 /* existing state overrides "show" argument */
1961 string s = track->gui_property ("visible");
1963 show = string_is_affirmative (s);
1966 /* this might or might not change the visibility status, so don't rely on it */
1967 track->set_marked_for_display (show);
1969 if (show && !no_redraw) {
1973 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1974 /* MIDI-related parameters are always in the menu, there's no
1975 reason to rebuild the menu just because we added a automation
1976 lane for one of them. But if we add a non-MIDI automation
1977 lane, then we need to invalidate the display menu.
1979 delete display_menu;
1985 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1987 boost::shared_ptr<Processor> processor (p.lock ());
1989 if (!processor || !processor->display_to_user ()) {
1993 /* we use this override to veto the Amp processor from the plugin menu,
1994 as its automation lane can be accessed using the special "Fader" menu
1998 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2002 using namespace Menu_Helpers;
2003 ProcessorAutomationInfo *rai;
2004 list<ProcessorAutomationInfo*>::iterator x;
2006 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2008 if (automatable.empty()) {
2012 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2013 if ((*x)->processor == processor) {
2018 if (x == processor_automation.end()) {
2020 rai = new ProcessorAutomationInfo (processor);
2021 processor_automation.push_back (rai);
2029 /* any older menu was deleted at the top of processors_changed()
2030 when we cleared the subplugin menu.
2033 rai->menu = manage (new Menu);
2034 MenuList& items = rai->menu->items();
2035 rai->menu->set_name ("ArdourContextMenu");
2039 std::set<Evoral::Parameter> has_visible_automation;
2040 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2042 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2044 ProcessorAutomationNode* pan;
2045 CheckMenuItem* mitem;
2047 string name = processor->describe_parameter (*i);
2049 items.push_back (CheckMenuElem (name));
2050 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2052 _subplugin_menu_map[*i] = mitem;
2054 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2055 mitem->set_active(true);
2058 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2062 pan = new ProcessorAutomationNode (*i, mitem, *this);
2064 rai->lines.push_back (pan);
2068 pan->menu_item = mitem;
2072 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2075 /* add the menu for this processor, because the subplugin
2076 menu is always cleared at the top of processors_changed().
2077 this is the result of some poor design in gtkmm and/or
2081 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2086 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2087 RouteTimeAxisView::ProcessorAutomationNode* pan)
2089 bool showit = pan->menu_item->get_active();
2090 bool redraw = false;
2092 if (pan->view == 0 && showit) {
2093 add_processor_automation_curve (rai->processor, pan->what);
2097 if (pan->view && pan->view->set_marked_for_display (showit)) {
2101 if (redraw && !no_redraw) {
2107 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2109 if (c.type == RouteProcessorChange::MeterPointChange) {
2110 /* nothing to do if only the meter point has changed */
2114 using namespace Menu_Helpers;
2116 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2117 (*i)->valid = false;
2120 setup_processor_menu_and_curves ();
2122 bool deleted_processor_automation = false;
2124 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2126 list<ProcessorAutomationInfo*>::iterator tmp;
2134 processor_automation.erase (i);
2135 deleted_processor_automation = true;
2142 if (deleted_processor_automation && !no_redraw) {
2147 boost::shared_ptr<AutomationLine>
2148 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2150 ProcessorAutomationNode* pan;
2152 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2158 return boost::shared_ptr<AutomationLine>();
2162 RouteTimeAxisView::reset_processor_automation_curves ()
2164 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2170 RouteTimeAxisView::update_rec_display ()
2172 RouteUI::update_rec_display ();
2173 name_entry.set_sensitive (!_route->record_enabled());
2177 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2179 if (_ignore_set_layer_display) {
2183 if (apply_to_selection) {
2184 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2188 _view->set_layer_display (d);
2191 set_gui_property (X_("layer-display"), enum_2_string (d));
2196 RouteTimeAxisView::layer_display () const
2199 return _view->layer_display ();
2202 /* we don't know, since we don't have a _view, so just return something */
2208 boost::shared_ptr<AutomationTimeAxisView>
2209 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2211 AutomationTracks::iterator i = _automation_tracks.find(param);
2212 if (i != _automation_tracks.end()) {
2215 return boost::shared_ptr<AutomationTimeAxisView>();
2220 RouteTimeAxisView::fast_update ()
2222 gm.get_level_meter().update_meters ();
2226 RouteTimeAxisView::hide_meter ()
2229 gm.get_level_meter().hide_meters ();
2233 RouteTimeAxisView::show_meter ()
2239 RouteTimeAxisView::reset_meter ()
2241 if (Config->get_show_track_meters()) {
2242 gm.get_level_meter().setup_meters (height-5);
2249 RouteTimeAxisView::clear_meter ()
2251 gm.get_level_meter().clear_meters ();
2255 RouteTimeAxisView::meter_changed ()
2257 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2262 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2268 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2270 using namespace Menu_Helpers;
2272 if (!_underlay_streams.empty()) {
2273 MenuList& parent_items = parent_menu->items();
2274 Menu* gs_menu = manage (new Menu);
2275 gs_menu->set_name ("ArdourContextMenu");
2276 MenuList& gs_items = gs_menu->items();
2278 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2280 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2281 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2282 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2288 RouteTimeAxisView::set_underlay_state()
2290 if (!underlay_xml_node) {
2294 XMLNodeList nlist = underlay_xml_node->children();
2295 XMLNodeConstIterator niter;
2296 XMLNode *child_node;
2298 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2299 child_node = *niter;
2301 if (child_node->name() != "Underlay") {
2305 XMLProperty* prop = child_node->property ("id");
2307 PBD::ID id (prop->value());
2309 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2312 add_underlay(v->view(), false);
2321 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2327 RouteTimeAxisView& other = v->trackview();
2329 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2330 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2331 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2335 _underlay_streams.push_back(v);
2336 other._underlay_mirrors.push_back(this);
2338 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2340 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2342 if (!underlay_xml_node) {
2343 underlay_xml_node = xml_node->add_child("Underlays");
2346 XMLNode* node = underlay_xml_node->add_child("Underlay");
2347 XMLProperty* prop = node->add_property("id");
2348 prop->set_value(v->trackview().route()->id().to_s());
2355 RouteTimeAxisView::remove_underlay (StreamView* v)
2361 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2362 RouteTimeAxisView& other = v->trackview();
2364 if (it != _underlay_streams.end()) {
2365 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2367 if (gm == other._underlay_mirrors.end()) {
2368 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2372 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2374 _underlay_streams.erase(it);
2375 other._underlay_mirrors.erase(gm);
2377 if (underlay_xml_node) {
2378 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2384 RouteTimeAxisView::set_button_names ()
2386 if (_route && _route->solo_safe()) {
2387 solo_button->remove ();
2388 if (solo_safe_pixbuf == 0) {
2389 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2391 solo_button->set_image (solo_safe_pixbuf);
2392 solo_button->set_text (string());
2394 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2395 if (Config->get_solo_control_is_listen_control()) {
2396 switch (Config->get_listen_position()) {
2397 case AfterFaderListen:
2398 solo_button->set_text (_("A"));
2399 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2401 case PreFaderListen:
2402 solo_button->set_text (_("P"));
2403 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2407 solo_button->set_text (_("s"));
2408 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2411 mute_button->set_text (_("m"));
2415 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2417 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2418 if (i != _main_automation_menu_map.end()) {
2422 i = _subplugin_menu_map.find (param);
2423 if (i != _subplugin_menu_map.end()) {
2431 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2433 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2435 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2439 gain_track.reset (new AutomationTimeAxisView (_session,
2440 _route, _route->amp(), c, param,
2445 _route->amp()->describe_parameter(param)));
2448 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2451 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2455 void add_region_to_list (RegionView* rv, RegionList* l)
2457 l->push_back (rv->region());
2461 RouteTimeAxisView::combine_regions ()
2463 /* as of may 2011, we do not offer uncombine for MIDI tracks
2466 if (!is_audio_track()) {
2474 RegionList selected_regions;
2475 boost::shared_ptr<Playlist> playlist = track()->playlist();
2477 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2479 if (selected_regions.size() < 2) {
2483 playlist->clear_changes ();
2484 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2486 _session->add_command (new StatefulDiffCommand (playlist));
2487 /* make the new region be selected */
2489 return _view->find_view (compound_region);
2493 RouteTimeAxisView::uncombine_regions ()
2495 /* as of may 2011, we do not offer uncombine for MIDI tracks
2497 if (!is_audio_track()) {
2505 RegionList selected_regions;
2506 boost::shared_ptr<Playlist> playlist = track()->playlist();
2508 /* have to grab selected regions first because the uncombine is going
2509 * to change that in the middle of the list traverse
2512 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2514 playlist->clear_changes ();
2516 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2517 playlist->uncombine (*i);
2520 _session->add_command (new StatefulDiffCommand (playlist));
2524 RouteTimeAxisView::state_id() const
2526 return string_compose ("rtav %1", _route->id().to_s());
2531 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2533 TimeAxisView::remove_child (c);
2535 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2537 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2538 if (i->second == a) {
2539 _automation_tracks.erase (i);