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 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
91 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
93 , parent_canvas (canvas)
96 , route_group_button (_("g"))
97 , playlist_button (_("p"))
98 , automation_button (_("a"))
99 , automation_action_menu (0)
100 , plugins_submenu_item (0)
101 , route_group_menu (0)
102 , playlist_action_menu (0)
104 , color_mode_menu (0)
105 , gm (sess, true, 125, 18)
106 , _ignore_set_layer_display (false)
111 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
113 RouteUI::set_route (rt);
115 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
116 gm.get_level_meter().set_no_show_all();
117 gm.get_level_meter().setup_meters(50);
118 gm.update_gain_sensitive ();
120 string str = gui_property ("height");
122 set_height (atoi (str));
124 set_height (preset_height (HeightNormal));
127 if (!_route->is_hidden()) {
128 if (gui_property ("visible").empty()) {
129 set_gui_property ("visible", true);
132 set_gui_property ("visible", false);
136 update_solo_display ();
138 timestretch_rect = 0;
141 ignore_toggle = false;
143 route_group_button.set_name ("route button");
144 playlist_button.set_name ("route button");
145 automation_button.set_name ("route button");
147 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
148 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
149 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
155 switch (track()->mode()) {
157 case ARDOUR::NonLayered:
158 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
160 case ARDOUR::Destructive:
161 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
165 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
167 if (is_midi_track()) {
168 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
170 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
173 rec_enable_button->set_sensitive (_session->writable());
175 /* set playlist button tip to the current playlist, and make it update when it changes */
176 update_playlist_tip ();
177 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
181 controls_hbox.pack_start(gm.get_level_meter(), false, false);
182 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
183 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
184 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
186 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
188 if (!_route->is_master()) {
189 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
193 // Gtk::VBox* pad = manage (new Gtk::VBox);
194 // pad->pack_start (gm.get_gain_slider(), false, false);
195 // pad->pack_start (*manage (new Gtk::Label), true, true);
197 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
199 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
200 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
201 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
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 */
237 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
238 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
239 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
241 PropertyList* plist = new PropertyList();
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 ("GainFader");
251 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
254 RouteTimeAxisView::~RouteTimeAxisView ()
256 CatchDeletion (this);
258 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
262 delete playlist_action_menu;
263 playlist_action_menu = 0;
268 _automation_tracks.clear ();
270 delete route_group_menu;
274 RouteTimeAxisView::post_construct ()
276 /* map current state of the route */
278 update_diskstream_display ();
279 setup_processor_menu_and_curves ();
280 reset_processor_automation_curves ();
283 /** Set up the processor menu for the current set of processors, and
284 * display automation curves for any parameters which have data.
287 RouteTimeAxisView::setup_processor_menu_and_curves ()
289 _subplugin_menu_map.clear ();
290 subplugin_menu.items().clear ();
291 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
292 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
296 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
298 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
299 if (_route->route_group()) {
300 _route->route_group()->remove (_route);
306 r.push_back (route ());
308 route_group_menu->build (r);
309 route_group_menu->menu()->popup (ev->button, ev->time);
315 RouteTimeAxisView::playlist_changed ()
321 RouteTimeAxisView::label_view ()
323 string x = _route->name();
325 if (x != name_entry.get_text()) {
326 name_entry.set_text (x);
329 if (x != name_label.get_text()) {
330 name_label.set_text (x);
333 ARDOUR_UI::instance()->set_tip (name_entry, Glib::Markup::escape_text(x));
337 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
339 if (what_changed.contains (ARDOUR::Properties::name)) {
345 RouteTimeAxisView::take_name_changed (void *src)
353 RouteTimeAxisView::playlist_click ()
355 build_playlist_menu ();
356 conditionally_add_to_selection ();
357 playlist_action_menu->popup (1, gtk_get_current_event_time());
361 RouteTimeAxisView::automation_click ()
363 conditionally_add_to_selection ();
364 build_automation_action_menu (false);
365 automation_action_menu->popup (1, gtk_get_current_event_time());
369 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
371 using namespace Menu_Helpers;
373 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
374 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
377 detach_menu (subplugin_menu);
379 _main_automation_menu_map.clear ();
380 delete automation_action_menu;
381 automation_action_menu = new Menu;
383 MenuList& items = automation_action_menu->items();
385 automation_action_menu->set_name ("ArdourContextMenu");
387 items.push_back (MenuElem (_("Show All Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
390 items.push_back (MenuElem (_("Show Existing Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
393 items.push_back (MenuElem (_("Hide All Automation"),
394 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
396 items.push_back (SeparatorElem ());
398 /* Attach the plugin submenu. It may have previously been used elsewhere,
399 so it was detached above
402 if (!subplugin_menu.items().empty()) {
403 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
404 items.back().set_sensitive (!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 _ignore_set_layer_display = true;
479 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
480 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
481 i->set_active (overlaid != 0 && stacked == 0);
482 i->set_inconsistent (overlaid != 0 && stacked != 0);
483 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
485 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
486 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
487 i->set_active (overlaid == 0 && stacked != 0);
488 i->set_inconsistent (overlaid != 0 && stacked != 0);
489 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
491 _ignore_set_layer_display = false;
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, int layers, int layer)
762 TimeAxisView::show_timestretch (start, end, layers, layer);
772 /* check that the time selection was made in our route, or our route group.
773 remember that route_group() == 0 implies the route is *not* in a edit group.
776 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
777 /* this doesn't apply to us */
781 /* ignore it if our edit group is not active */
783 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
788 if (timestretch_rect == 0) {
789 timestretch_rect = new SimpleRect (*canvas_display ());
790 timestretch_rect->property_x1() = 0.0;
791 timestretch_rect->property_y1() = 0.0;
792 timestretch_rect->property_x2() = 0.0;
793 timestretch_rect->property_y2() = 0.0;
794 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
795 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
798 timestretch_rect->show ();
799 timestretch_rect->raise_to_top ();
801 double const x1 = start / _editor.get_current_zoom();
802 double const x2 = (end - 1) / _editor.get_current_zoom();
804 timestretch_rect->property_x1() = x1;
805 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
806 timestretch_rect->property_x2() = x2;
807 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
811 RouteTimeAxisView::hide_timestretch ()
813 TimeAxisView::hide_timestretch ();
815 if (timestretch_rect) {
816 timestretch_rect->hide ();
821 RouteTimeAxisView::show_selection (TimeSelection& ts)
825 /* ignore it if our edit group is not active or if the selection was started
826 in some other track or route group (remember that route_group() == 0 means
827 that the track is not in an route group).
830 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
831 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
837 TimeAxisView::show_selection (ts);
841 RouteTimeAxisView::set_height (uint32_t h)
844 bool height_changed = (height == 0) || (h != height);
845 gm.get_level_meter().setup_meters (gmlen);
847 TimeAxisView::set_height (h);
850 _view->set_height ((double) current_height());
853 if (height >= preset_height (HeightNormal)) {
857 gm.get_gain_slider().show();
859 if (!_route || _route->is_monitor()) {
864 if (rec_enable_button)
865 rec_enable_button->show();
867 route_group_button.show();
868 automation_button.show();
870 if (is_track() && track()->mode() == ARDOUR::Normal) {
871 playlist_button.show();
878 gm.get_gain_slider().hide();
880 if (!_route || _route->is_monitor()) {
885 if (rec_enable_button)
886 rec_enable_button->show();
888 route_group_button.hide ();
889 automation_button.hide ();
891 if (is_track() && track()->mode() == ARDOUR::Normal) {
892 playlist_button.hide ();
897 if (height_changed && !no_redraw) {
898 /* only emit the signal if the height really changed */
904 RouteTimeAxisView::route_color_changed ()
907 _view->apply_color (color(), StreamView::RegionColor);
912 RouteTimeAxisView::reset_samples_per_unit ()
914 set_samples_per_unit (_editor.get_current_zoom());
918 RouteTimeAxisView::horizontal_position_changed ()
921 _view->horizontal_position_changed ();
926 RouteTimeAxisView::set_samples_per_unit (double spu)
931 speed = track()->speed();
935 _view->set_samples_per_unit (spu * speed);
938 TimeAxisView::set_samples_per_unit (spu * speed);
942 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
944 if (!mitem->get_active()) {
945 /* this is one of the two calls made when these radio menu items change status. this one
946 is for the item that became inactive, and we want to ignore it.
951 if (apply_to_selection) {
952 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
955 track()->set_align_choice (choice);
961 RouteTimeAxisView::rename_current_playlist ()
963 ArdourPrompter prompter (true);
966 boost::shared_ptr<Track> tr = track();
967 if (!tr || tr->destructive()) {
971 boost::shared_ptr<Playlist> pl = tr->playlist();
976 prompter.set_title (_("Rename Playlist"));
977 prompter.set_prompt (_("New name for playlist:"));
978 prompter.set_initial_text (pl->name());
979 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
980 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
982 switch (prompter.run ()) {
983 case Gtk::RESPONSE_ACCEPT:
984 prompter.get_result (name);
996 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
998 std::string ret (basename);
1000 std::string const group_string = "." + route_group()->name() + ".";
1002 // iterate through all playlists
1004 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1005 std::string tmp = (*i)->name();
1007 std::string::size_type idx = tmp.find(group_string);
1008 // find those which belong to this group
1009 if (idx != string::npos) {
1010 tmp = tmp.substr(idx + group_string.length());
1012 // and find the largest current number
1013 int x = atoi(tmp.c_str());
1014 if (x > maxnumber) {
1023 snprintf (buf, sizeof(buf), "%d", maxnumber);
1025 ret = this->name() + "." + route_group()->name () + "." + buf;
1031 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1035 boost::shared_ptr<Track> tr = track ();
1036 if (!tr || tr->destructive()) {
1040 boost::shared_ptr<const Playlist> pl = tr->playlist();
1047 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1048 name = resolve_new_group_playlist_name(name, playlists_before_op);
1051 while (_session->playlists->by_name(name)) {
1052 name = Playlist::bump_name (name, *_session);
1055 // TODO: The prompter "new" button should be de-activated if the user
1056 // specifies a playlist name which already exists in the session.
1060 ArdourPrompter prompter (true);
1062 prompter.set_title (_("New Copy Playlist"));
1063 prompter.set_prompt (_("Name for new playlist:"));
1064 prompter.set_initial_text (name);
1065 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1066 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1067 prompter.show_all ();
1069 switch (prompter.run ()) {
1070 case Gtk::RESPONSE_ACCEPT:
1071 prompter.get_result (name);
1079 if (name.length()) {
1080 tr->use_copy_playlist ();
1081 tr->playlist()->set_name (name);
1086 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1090 boost::shared_ptr<Track> tr = track ();
1091 if (!tr || tr->destructive()) {
1095 boost::shared_ptr<const Playlist> pl = tr->playlist();
1102 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1103 name = resolve_new_group_playlist_name(name,playlists_before_op);
1106 while (_session->playlists->by_name(name)) {
1107 name = Playlist::bump_name (name, *_session);
1113 ArdourPrompter prompter (true);
1115 prompter.set_title (_("New Playlist"));
1116 prompter.set_prompt (_("Name for new playlist:"));
1117 prompter.set_initial_text (name);
1118 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1119 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1121 switch (prompter.run ()) {
1122 case Gtk::RESPONSE_ACCEPT:
1123 prompter.get_result (name);
1131 if (name.length()) {
1132 tr->use_new_playlist ();
1133 tr->playlist()->set_name (name);
1138 RouteTimeAxisView::clear_playlist ()
1140 boost::shared_ptr<Track> tr = track ();
1141 if (!tr || tr->destructive()) {
1145 boost::shared_ptr<Playlist> pl = tr->playlist();
1150 _editor.clear_playlist (pl);
1154 RouteTimeAxisView::speed_changed ()
1156 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1160 RouteTimeAxisView::update_diskstream_display ()
1170 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1172 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1174 /* special case: select/deselect all tracks */
1175 if (_editor.get_selection().selected (this)) {
1176 _editor.get_selection().clear_tracks ();
1178 _editor.select_all_tracks ();
1184 switch (ArdourKeyboard::selection_type (ev->state)) {
1185 case Selection::Toggle:
1186 _editor.get_selection().toggle (this);
1189 case Selection::Set:
1190 _editor.get_selection().set (this);
1193 case Selection::Extend:
1194 _editor.extend_selection_to_track (*this);
1197 case Selection::Add:
1198 _editor.get_selection().add (this);
1204 RouteTimeAxisView::set_selected_points (PointSelection& points)
1206 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1207 (*i)->set_selected_points (points);
1212 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1215 _view->set_selected_regionviews (regions);
1219 /** Add the selectable things that we have to a list.
1220 * @param results List to add things to.
1223 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1228 speed = track()->speed();
1231 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1232 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1234 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1235 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1238 /* pick up visible automation tracks */
1240 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1241 if (!(*i)->hidden()) {
1242 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1248 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1251 _view->get_inverted_selectables (sel, results);
1254 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1255 if (!(*i)->hidden()) {
1256 (*i)->get_inverted_selectables (sel, results);
1264 RouteTimeAxisView::route_group () const
1266 return _route->route_group();
1270 RouteTimeAxisView::name() const
1272 return _route->name();
1275 boost::shared_ptr<Playlist>
1276 RouteTimeAxisView::playlist () const
1278 boost::shared_ptr<Track> tr;
1280 if ((tr = track()) != 0) {
1281 return tr->playlist();
1283 return boost::shared_ptr<Playlist> ();
1288 RouteTimeAxisView::name_entry_changed ()
1290 string x = name_entry.get_text ();
1292 if (x == _route->name()) {
1296 strip_whitespace_edges (x);
1298 if (x.length() == 0) {
1299 name_entry.set_text (_route->name());
1303 if (_session->route_name_internal (x)) {
1304 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1306 name_entry.grab_focus ();
1307 } else if (RouteUI::verify_new_route_name (x)) {
1308 _route->set_name (x);
1310 name_entry.grab_focus ();
1314 boost::shared_ptr<Region>
1315 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1317 boost::shared_ptr<Playlist> pl = playlist ();
1320 return pl->find_next_region (pos, point, dir);
1323 return boost::shared_ptr<Region> ();
1327 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1329 boost::shared_ptr<Playlist> pl = playlist ();
1332 return pl->find_next_region_boundary (pos, dir);
1339 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1341 boost::shared_ptr<Playlist> what_we_got;
1342 boost::shared_ptr<Track> tr = track ();
1343 boost::shared_ptr<Playlist> playlist;
1346 /* route is a bus, not a track */
1350 playlist = tr->playlist();
1352 TimeSelection time (selection.time);
1353 float const speed = tr->speed();
1354 if (speed != 1.0f) {
1355 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1356 (*i).start = session_frame_to_track_frame((*i).start, speed);
1357 (*i).end = session_frame_to_track_frame((*i).end, speed);
1361 playlist->clear_changes ();
1362 playlist->clear_owned_changes ();
1366 if (playlist->cut (time) != 0) {
1367 vector<Command*> cmds;
1368 playlist->rdiff (cmds);
1369 _session->add_commands (cmds);
1371 _session->add_command (new StatefulDiffCommand (playlist));
1376 if ((what_we_got = playlist->cut (time)) != 0) {
1377 _editor.get_cut_buffer().add (what_we_got);
1378 vector<Command*> cmds;
1379 playlist->rdiff (cmds);
1380 _session->add_commands (cmds);
1382 _session->add_command (new StatefulDiffCommand (playlist));
1386 if ((what_we_got = playlist->copy (time)) != 0) {
1387 _editor.get_cut_buffer().add (what_we_got);
1392 if ((what_we_got = playlist->cut (time)) != 0) {
1394 vector<Command*> cmds;
1395 playlist->rdiff (cmds);
1396 _session->add_commands (cmds);
1397 _session->add_command (new StatefulDiffCommand (playlist));
1398 what_we_got->release ();
1405 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1411 boost::shared_ptr<Playlist> pl = playlist ();
1412 PlaylistSelection::iterator p;
1414 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1416 if (p == selection.playlists.end()) {
1420 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1422 if (track()->speed() != 1.0f) {
1423 pos = session_frame_to_track_frame (pos, track()->speed());
1424 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1427 pl->clear_changes ();
1428 pl->paste (*p, pos, times);
1429 _session->add_command (new StatefulDiffCommand (pl));
1435 struct PlaylistSorter {
1436 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1437 return a->sort_id() < b->sort_id();
1442 RouteTimeAxisView::build_playlist_menu ()
1444 using namespace Menu_Helpers;
1450 delete playlist_action_menu;
1451 playlist_action_menu = new Menu;
1452 playlist_action_menu->set_name ("ArdourContextMenu");
1454 MenuList& playlist_items = playlist_action_menu->items();
1455 playlist_action_menu->set_name ("ArdourContextMenu");
1456 playlist_items.clear();
1458 RadioMenuItem::Group playlist_group;
1459 boost::shared_ptr<Track> tr = track ();
1461 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1463 /* sort the playlists */
1465 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1467 /* add the playlists to the menu */
1468 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1469 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1470 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1471 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1473 if (tr->playlist()->id() == (*i)->id()) {
1479 playlist_items.push_back (SeparatorElem());
1480 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1481 playlist_items.push_back (SeparatorElem());
1483 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1484 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1485 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1488 // Use a label which tells the user what is happening
1489 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1490 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1494 playlist_items.push_back (SeparatorElem());
1495 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1496 playlist_items.push_back (SeparatorElem());
1498 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1502 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1504 assert (is_track());
1506 // exit if we were triggered by deactivating the old playlist
1507 if (!item->get_active()) {
1511 boost::shared_ptr<Playlist> pl (wpl.lock());
1517 if (track()->playlist() == pl) {
1518 // exit when use_playlist is called by the creation of the playlist menu
1519 // or the playlist choice is unchanged
1523 track()->use_playlist (pl);
1525 RouteGroup* rg = route_group();
1527 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1528 std::string group_string = "." + rg->name() + ".";
1530 std::string take_name = pl->name();
1531 std::string::size_type idx = take_name.find(group_string);
1533 if (idx == std::string::npos)
1536 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1538 boost::shared_ptr<RouteList> rl (rg->route_list());
1540 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1541 if ((*i) == this->route()) {
1545 std::string playlist_name = (*i)->name()+group_string+take_name;
1547 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1552 if (track->freeze_state() == Track::Frozen) {
1553 /* Don't change playlists of frozen tracks */
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::update_playlist_tip ()
1572 RouteGroup* rg = route_group ();
1573 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1574 string group_string = "." + rg->name() + ".";
1576 string take_name = track()->playlist()->name();
1577 string::size_type idx = take_name.find(group_string);
1579 if (idx != string::npos) {
1580 /* find the bit containing the take number / name */
1581 take_name = take_name.substr (idx + group_string.length());
1583 /* set the playlist button tooltip to the take name */
1584 ARDOUR_UI::instance()->set_tip (
1586 string_compose(_("Take: %1.%2"),
1587 Glib::Markup::escape_text(rg->name()),
1588 Glib::Markup::escape_text(take_name))
1595 /* set the playlist button tooltip to the playlist name */
1596 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1601 RouteTimeAxisView::show_playlist_selector ()
1603 _editor.playlist_selector().show_for (this);
1607 RouteTimeAxisView::map_frozen ()
1613 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1615 switch (track()->freeze_state()) {
1617 playlist_button.set_sensitive (false);
1618 rec_enable_button->set_sensitive (false);
1621 playlist_button.set_sensitive (true);
1622 rec_enable_button->set_sensitive (true);
1628 RouteTimeAxisView::color_handler ()
1630 //case cTimeStretchOutline:
1631 if (timestretch_rect) {
1632 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1634 //case cTimeStretchFill:
1635 if (timestretch_rect) {
1636 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1642 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1643 * Will add track if necessary.
1646 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1648 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1649 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1652 /* it doesn't exist yet, so we don't care about the button state: just add it */
1653 create_automation_child (param, true);
1656 bool yn = menu->get_active();
1657 bool changed = false;
1659 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1661 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1662 will have done that for us.
1665 if (changed && !no_redraw) {
1673 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1675 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1681 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1683 if (menu && !_hidden) {
1684 ignore_toggle = true;
1685 menu->set_active (false);
1686 ignore_toggle = false;
1689 if (_route && !no_redraw) {
1696 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1698 if (apply_to_selection) {
1699 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1703 /* Show our automation */
1705 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1706 i->second->set_marked_for_display (true);
1708 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1711 menu->set_active(true);
1716 /* Show processor automation */
1718 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1719 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1720 if ((*ii)->view == 0) {
1721 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1724 (*ii)->menu_item->set_active (true);
1737 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1739 if (apply_to_selection) {
1740 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1744 /* Show our automation */
1746 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1747 if (i->second->has_automation()) {
1748 i->second->set_marked_for_display (true);
1750 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1752 menu->set_active(true);
1757 /* Show processor automation */
1759 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1760 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1761 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1762 (*ii)->menu_item->set_active (true);
1774 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1776 if (apply_to_selection) {
1777 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1781 /* Hide our automation */
1783 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1784 i->second->set_marked_for_display (false);
1786 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1789 menu->set_active (false);
1793 /* Hide processor automation */
1795 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1796 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1797 (*ii)->menu_item->set_active (false);
1808 RouteTimeAxisView::region_view_added (RegionView* rv)
1810 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1811 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1812 boost::shared_ptr<AutomationTimeAxisView> atv;
1814 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1819 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1820 (*i)->add_ghost(rv);
1824 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1826 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1832 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1834 parent.remove_processor_automation_node (this);
1838 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1841 remove_child (pan->view);
1845 RouteTimeAxisView::ProcessorAutomationNode*
1846 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1848 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1850 if ((*i)->processor == processor) {
1852 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1853 if ((*ii)->what == what) {
1863 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1865 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1868 ProcessorAutomationNode* pan;
1870 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1871 /* session state may never have been saved with new plugin */
1872 error << _("programming error: ")
1873 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1874 processor->name(), what.type(), (int) what.channel(), what.id() )
1884 boost::shared_ptr<AutomationControl> control
1885 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1887 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1888 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1889 _editor, *this, false, parent_canvas,
1890 processor->describe_parameter (what), processor->name()));
1892 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1894 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1897 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1902 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1905 pan->menu_item->set_active (false);
1914 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1916 boost::shared_ptr<Processor> processor (p.lock ());
1918 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1919 /* The Amp processor is a special case and is dealt with separately */
1923 set<Evoral::Parameter> existing;
1925 processor->what_has_data (existing);
1927 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1929 Evoral::Parameter param (*i);
1930 boost::shared_ptr<AutomationLine> al;
1932 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1935 add_processor_automation_curve (processor, param);
1941 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1943 using namespace Menu_Helpers;
1947 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1949 _automation_tracks[param] = track;
1951 /* existing state overrides "show" argument */
1952 string s = track->gui_property ("visible");
1954 show = string_is_affirmative (s);
1957 /* this might or might not change the visibility status, so don't rely on it */
1958 track->set_marked_for_display (show);
1960 if (show && !no_redraw) {
1964 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1965 /* MIDI-related parameters are always in the menu, there's no
1966 reason to rebuild the menu just because we added a automation
1967 lane for one of them. But if we add a non-MIDI automation
1968 lane, then we need to invalidate the display menu.
1970 delete display_menu;
1976 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1978 boost::shared_ptr<Processor> processor (p.lock ());
1980 if (!processor || !processor->display_to_user ()) {
1984 /* we use this override to veto the Amp processor from the plugin menu,
1985 as its automation lane can be accessed using the special "Fader" menu
1989 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1993 using namespace Menu_Helpers;
1994 ProcessorAutomationInfo *rai;
1995 list<ProcessorAutomationInfo*>::iterator x;
1997 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1999 if (automatable.empty()) {
2003 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2004 if ((*x)->processor == processor) {
2009 if (x == processor_automation.end()) {
2011 rai = new ProcessorAutomationInfo (processor);
2012 processor_automation.push_back (rai);
2020 /* any older menu was deleted at the top of processors_changed()
2021 when we cleared the subplugin menu.
2024 rai->menu = manage (new Menu);
2025 MenuList& items = rai->menu->items();
2026 rai->menu->set_name ("ArdourContextMenu");
2030 std::set<Evoral::Parameter> has_visible_automation;
2031 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2033 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2035 ProcessorAutomationNode* pan;
2036 CheckMenuItem* mitem;
2038 string name = processor->describe_parameter (*i);
2040 items.push_back (CheckMenuElem (name));
2041 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2043 _subplugin_menu_map[*i] = mitem;
2045 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2046 mitem->set_active(true);
2049 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2053 pan = new ProcessorAutomationNode (*i, mitem, *this);
2055 rai->lines.push_back (pan);
2059 pan->menu_item = mitem;
2063 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2066 /* add the menu for this processor, because the subplugin
2067 menu is always cleared at the top of processors_changed().
2068 this is the result of some poor design in gtkmm and/or
2072 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2077 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2078 RouteTimeAxisView::ProcessorAutomationNode* pan)
2080 bool showit = pan->menu_item->get_active();
2081 bool redraw = false;
2083 if (pan->view == 0 && showit) {
2084 add_processor_automation_curve (rai->processor, pan->what);
2088 if (pan->view && pan->view->set_marked_for_display (showit)) {
2092 if (redraw && !no_redraw) {
2098 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2100 if (c.type == RouteProcessorChange::MeterPointChange) {
2101 /* nothing to do if only the meter point has changed */
2105 using namespace Menu_Helpers;
2107 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2108 (*i)->valid = false;
2111 setup_processor_menu_and_curves ();
2113 bool deleted_processor_automation = false;
2115 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2117 list<ProcessorAutomationInfo*>::iterator tmp;
2125 processor_automation.erase (i);
2126 deleted_processor_automation = true;
2133 if (deleted_processor_automation && !no_redraw) {
2138 boost::shared_ptr<AutomationLine>
2139 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2141 ProcessorAutomationNode* pan;
2143 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2149 return boost::shared_ptr<AutomationLine>();
2153 RouteTimeAxisView::reset_processor_automation_curves ()
2155 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2161 RouteTimeAxisView::can_edit_name () const
2163 /* we do not allow track name changes if it is record enabled
2165 return !_route->record_enabled();
2169 RouteTimeAxisView::update_rec_display ()
2171 RouteUI::update_rec_display ();
2173 if (_route->record_enabled()) {
2183 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2185 if (_ignore_set_layer_display) {
2189 if (apply_to_selection) {
2190 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2194 _view->set_layer_display (d);
2197 set_gui_property (X_("layer-display"), enum_2_string (d));
2202 RouteTimeAxisView::layer_display () const
2205 return _view->layer_display ();
2208 /* we don't know, since we don't have a _view, so just return something */
2214 boost::shared_ptr<AutomationTimeAxisView>
2215 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2217 AutomationTracks::iterator i = _automation_tracks.find(param);
2218 if (i != _automation_tracks.end()) {
2221 return boost::shared_ptr<AutomationTimeAxisView>();
2226 RouteTimeAxisView::fast_update ()
2228 gm.get_level_meter().update_meters ();
2232 RouteTimeAxisView::hide_meter ()
2235 gm.get_level_meter().hide_meters ();
2239 RouteTimeAxisView::show_meter ()
2245 RouteTimeAxisView::reset_meter ()
2247 if (Config->get_show_track_meters()) {
2248 gm.get_level_meter().setup_meters (height-5);
2255 RouteTimeAxisView::clear_meter ()
2257 gm.get_level_meter().clear_meters ();
2261 RouteTimeAxisView::meter_changed ()
2263 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2268 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2274 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2276 using namespace Menu_Helpers;
2278 if (!_underlay_streams.empty()) {
2279 MenuList& parent_items = parent_menu->items();
2280 Menu* gs_menu = manage (new Menu);
2281 gs_menu->set_name ("ArdourContextMenu");
2282 MenuList& gs_items = gs_menu->items();
2284 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2286 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2287 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2288 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2294 RouteTimeAxisView::set_underlay_state()
2296 if (!underlay_xml_node) {
2300 XMLNodeList nlist = underlay_xml_node->children();
2301 XMLNodeConstIterator niter;
2302 XMLNode *child_node;
2304 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2305 child_node = *niter;
2307 if (child_node->name() != "Underlay") {
2311 XMLProperty* prop = child_node->property ("id");
2313 PBD::ID id (prop->value());
2315 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2318 add_underlay(v->view(), false);
2327 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2333 RouteTimeAxisView& other = v->trackview();
2335 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2336 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2337 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2341 _underlay_streams.push_back(v);
2342 other._underlay_mirrors.push_back(this);
2344 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2346 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2348 if (!underlay_xml_node) {
2349 underlay_xml_node = xml_node->add_child("Underlays");
2352 XMLNode* node = underlay_xml_node->add_child("Underlay");
2353 XMLProperty* prop = node->add_property("id");
2354 prop->set_value(v->trackview().route()->id().to_s());
2361 RouteTimeAxisView::remove_underlay (StreamView* v)
2367 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2368 RouteTimeAxisView& other = v->trackview();
2370 if (it != _underlay_streams.end()) {
2371 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2373 if (gm == other._underlay_mirrors.end()) {
2374 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2378 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2380 _underlay_streams.erase(it);
2381 other._underlay_mirrors.erase(gm);
2383 if (underlay_xml_node) {
2384 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2390 RouteTimeAxisView::set_button_names ()
2392 if (_route && _route->solo_safe()) {
2393 solo_button->remove ();
2394 if (solo_safe_pixbuf == 0) {
2395 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2397 solo_button->set_image (solo_safe_pixbuf);
2398 solo_button->set_text (string());
2400 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2401 if (Config->get_solo_control_is_listen_control()) {
2402 switch (Config->get_listen_position()) {
2403 case AfterFaderListen:
2404 solo_button->set_text (_("A"));
2405 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2407 case PreFaderListen:
2408 solo_button->set_text (_("P"));
2409 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2413 solo_button->set_text (_("s"));
2414 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2417 mute_button->set_text (_("m"));
2421 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2423 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2424 if (i != _main_automation_menu_map.end()) {
2428 i = _subplugin_menu_map.find (param);
2429 if (i != _subplugin_menu_map.end()) {
2437 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2439 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2441 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2445 gain_track.reset (new AutomationTimeAxisView (_session,
2446 _route, _route->amp(), c, param,
2451 _route->amp()->describe_parameter(param)));
2454 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2457 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2461 void add_region_to_list (RegionView* rv, RegionList* l)
2463 l->push_back (rv->region());
2467 RouteTimeAxisView::combine_regions ()
2469 /* as of may 2011, we do not offer uncombine for MIDI tracks
2472 if (!is_audio_track()) {
2480 RegionList selected_regions;
2481 boost::shared_ptr<Playlist> playlist = track()->playlist();
2483 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2485 if (selected_regions.size() < 2) {
2489 playlist->clear_changes ();
2490 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2492 _session->add_command (new StatefulDiffCommand (playlist));
2493 /* make the new region be selected */
2495 return _view->find_view (compound_region);
2499 RouteTimeAxisView::uncombine_regions ()
2501 /* as of may 2011, we do not offer uncombine for MIDI tracks
2503 if (!is_audio_track()) {
2511 RegionList selected_regions;
2512 boost::shared_ptr<Playlist> playlist = track()->playlist();
2514 /* have to grab selected regions first because the uncombine is going
2515 * to change that in the middle of the list traverse
2518 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2520 playlist->clear_changes ();
2522 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2523 playlist->uncombine (*i);
2526 _session->add_command (new StatefulDiffCommand (playlist));
2530 RouteTimeAxisView::state_id() const
2532 return string_compose ("rtav %1", _route->id().to_s());
2537 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2539 TimeAxisView::remove_child (c);
2541 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2543 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2544 if (i->second == a) {
2545 _automation_tracks.erase (i);