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, 125)
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 // Gtk::VBox* pad = manage (new Gtk::VBox);
209 // pad->pack_start (gm.get_gain_slider(), false, false);
210 // pad->pack_start (*manage (new Gtk::Label), true, true);
212 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
214 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
215 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
216 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
218 if (is_midi_track()) {
219 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
221 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
226 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
228 if (is_track() && track()->mode() == ARDOUR::Normal) {
229 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
234 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
235 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
239 str = gui_property ("layer-display");
241 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
244 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
245 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
247 /* pick up the correct freeze state */
251 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
252 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
253 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
255 PropertyList* plist = new PropertyList();
257 plist->add (ARDOUR::Properties::edit, true);
258 plist->add (ARDOUR::Properties::mute, true);
259 plist->add (ARDOUR::Properties::solo, true);
261 route_group_menu = new RouteGroupMenu (_session, plist);
263 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
264 gm.get_gain_slider().set_name ("GainFader");
266 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
272 RouteTimeAxisView::~RouteTimeAxisView ()
274 CatchDeletion (this);
276 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
280 delete playlist_action_menu;
281 playlist_action_menu = 0;
286 _automation_tracks.clear ();
288 delete route_group_menu;
292 RouteTimeAxisView::post_construct ()
294 /* map current state of the route */
296 update_diskstream_display ();
297 setup_processor_menu_and_curves ();
298 reset_processor_automation_curves ();
301 /** Set up the processor menu for the current set of processors, and
302 * display automation curves for any parameters which have data.
305 RouteTimeAxisView::setup_processor_menu_and_curves ()
307 _subplugin_menu_map.clear ();
308 subplugin_menu.items().clear ();
309 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
310 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
314 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
316 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
317 if (_route->route_group()) {
318 _route->route_group()->remove (_route);
324 r.push_back (route ());
326 route_group_menu->build (r);
327 route_group_menu->menu()->popup (ev->button, ev->time);
333 RouteTimeAxisView::playlist_changed ()
339 RouteTimeAxisView::label_view ()
341 string x = _route->name();
343 if (x != name_entry.get_text()) {
344 name_entry.set_text (x);
347 if (x != name_label.get_text()) {
348 name_label.set_text (x);
351 ARDOUR_UI::instance()->set_tip (name_entry, Glib::Markup::escape_text(x));
355 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
357 if (what_changed.contains (ARDOUR::Properties::name)) {
363 RouteTimeAxisView::take_name_changed (void *src)
371 RouteTimeAxisView::playlist_click ()
373 build_playlist_menu ();
374 conditionally_add_to_selection ();
375 playlist_action_menu->popup (1, gtk_get_current_event_time());
379 RouteTimeAxisView::automation_click ()
381 conditionally_add_to_selection ();
382 build_automation_action_menu (false);
383 automation_action_menu->popup (1, gtk_get_current_event_time());
387 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
389 using namespace Menu_Helpers;
391 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
392 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
395 detach_menu (subplugin_menu);
397 _main_automation_menu_map.clear ();
398 delete automation_action_menu;
399 automation_action_menu = new Menu;
401 MenuList& items = automation_action_menu->items();
403 automation_action_menu->set_name ("ArdourContextMenu");
405 items.push_back (MenuElem (_("Show All Automation"),
406 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
408 items.push_back (MenuElem (_("Show Existing Automation"),
409 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
411 items.push_back (MenuElem (_("Hide All Automation"),
412 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
414 items.push_back (SeparatorElem ());
416 /* Attach the plugin submenu. It may have previously been used elsewhere,
417 so it was detached above
420 if (!subplugin_menu.items().empty()) {
421 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
422 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
427 RouteTimeAxisView::build_display_menu ()
429 using namespace Menu_Helpers;
433 TimeAxisView::build_display_menu ();
435 /* now fill it with our stuff */
437 MenuList& items = display_menu->items();
438 display_menu->set_name ("ArdourContextMenu");
440 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
443 detach_menu (*_size_menu);
446 items.push_back (MenuElem (_("Height"), *_size_menu));
448 items.push_back (SeparatorElem());
450 if (!Profile->get_sae()) {
451 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
452 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
453 items.push_back (SeparatorElem());
456 // Hook for derived classes to add type specific stuff
457 append_extra_display_menu_items ();
461 Menu* layers_menu = manage (new Menu);
462 MenuList &layers_items = layers_menu->items();
463 layers_menu->set_name("ArdourContextMenu");
465 RadioMenuItem::Group layers_group;
467 /* Find out how many overlaid/stacked tracks we have in the selection */
471 TrackSelection const & s = _editor.get_selection().tracks;
472 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
473 StreamView* v = (*i)->view ();
478 switch (v->layer_display ()) {
489 /* We're not connecting to signal_toggled() here; in the case where these two items are
490 set to be in the `inconsistent' state, it seems that one or other will end up active
491 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
492 select the active one, no toggled signal is emitted so nothing happens.
495 _ignore_set_layer_display = true;
497 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
498 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
499 i->set_active (overlaid != 0 && stacked == 0);
500 i->set_inconsistent (overlaid != 0 && stacked != 0);
501 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
503 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
504 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
505 i->set_active (overlaid == 0 && stacked != 0);
506 i->set_inconsistent (overlaid != 0 && stacked != 0);
507 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
509 _ignore_set_layer_display = false;
511 items.push_back (MenuElem (_("Layers"), *layers_menu));
513 if (!Profile->get_sae()) {
515 Menu* alignment_menu = manage (new Menu);
516 MenuList& alignment_items = alignment_menu->items();
517 alignment_menu->set_name ("ArdourContextMenu");
519 RadioMenuItem::Group align_group;
521 /* Same verbose hacks as for the layering options above */
527 boost::shared_ptr<Track> first_track;
529 TrackSelection const & s = _editor.get_selection().tracks;
530 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
531 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
532 if (!r || !r->is_track ()) {
537 first_track = r->track();
540 switch (r->track()->alignment_choice()) {
544 switch (r->track()->alignment_style()) {
545 case ExistingMaterial:
553 case UseExistingMaterial:
569 inconsistent = false;
578 if (!inconsistent && first_track) {
580 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
581 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
582 i->set_active (automatic != 0 && existing == 0 && capture == 0);
583 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
585 switch (first_track->alignment_choice()) {
587 switch (first_track->alignment_style()) {
588 case ExistingMaterial:
589 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
592 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
600 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
601 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
602 i->set_active (existing != 0 && capture == 0 && automatic == 0);
603 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
605 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
606 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
607 i->set_active (existing == 0 && capture != 0 && automatic == 0);
608 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
610 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
616 Menu* mode_menu = manage (new Menu);
617 MenuList& mode_items = mode_menu->items ();
618 mode_menu->set_name ("ArdourContextMenu");
620 RadioMenuItem::Group mode_group;
626 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
627 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
628 if (!r || !r->is_track ()) {
632 switch (r->track()->mode()) {
645 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
646 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
648 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
649 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
651 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
652 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
653 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
654 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
655 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
657 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
658 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
659 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
660 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
661 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
663 items.push_back (MenuElem (_("Mode"), *mode_menu));
666 color_mode_menu = build_color_mode_menu();
667 if (color_mode_menu) {
668 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
671 items.push_back (SeparatorElem());
673 build_playlist_menu ();
674 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
675 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
678 route_group_menu->detach ();
681 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
682 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
684 r.push_back (rtv->route ());
689 r.push_back (route ());
692 route_group_menu->build (r);
693 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
695 build_automation_action_menu (true);
696 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
698 items.push_back (SeparatorElem());
702 TrackSelection const & s = _editor.get_selection().tracks;
703 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
704 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
709 if (r->route()->active()) {
716 items.push_back (CheckMenuElem (_("Active")));
717 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
718 bool click_sets_active = true;
719 if (active > 0 && inactive == 0) {
720 i->set_active (true);
721 click_sets_active = false;
722 } else if (active > 0 && inactive > 0) {
723 i->set_inconsistent (true);
725 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
727 items.push_back (SeparatorElem());
728 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
729 if (!Profile->get_sae()) {
730 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
732 items.push_front (SeparatorElem());
733 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
738 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
740 if (apply_to_selection) {
741 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
746 if (!track()->can_use_mode (mode, needs_bounce)) {
752 cerr << "would bounce this one\n";
757 track()->set_mode (mode);
759 rec_enable_button->remove ();
762 case ARDOUR::NonLayered:
764 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
765 rec_enable_button->set_text (string());
767 case ARDOUR::Destructive:
768 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
769 rec_enable_button->set_text (string());
773 rec_enable_button->show_all ();
778 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
780 TimeAxisView::show_timestretch (start, end, layers, layer);
790 /* check that the time selection was made in our route, or our route group.
791 remember that route_group() == 0 implies the route is *not* in a edit group.
794 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
795 /* this doesn't apply to us */
799 /* ignore it if our edit group is not active */
801 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
806 if (timestretch_rect == 0) {
807 timestretch_rect = new SimpleRect (*canvas_display ());
808 timestretch_rect->property_x1() = 0.0;
809 timestretch_rect->property_y1() = 0.0;
810 timestretch_rect->property_x2() = 0.0;
811 timestretch_rect->property_y2() = 0.0;
812 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
813 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
816 timestretch_rect->show ();
817 timestretch_rect->raise_to_top ();
819 double const x1 = start / _editor.get_current_zoom();
820 double const x2 = (end - 1) / _editor.get_current_zoom();
822 timestretch_rect->property_x1() = x1;
823 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
824 timestretch_rect->property_x2() = x2;
825 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
829 RouteTimeAxisView::hide_timestretch ()
831 TimeAxisView::hide_timestretch ();
833 if (timestretch_rect) {
834 timestretch_rect->hide ();
839 RouteTimeAxisView::show_selection (TimeSelection& ts)
843 /* ignore it if our edit group is not active or if the selection was started
844 in some other track or route group (remember that route_group() == 0 means
845 that the track is not in an route group).
848 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
849 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
855 TimeAxisView::show_selection (ts);
859 RouteTimeAxisView::set_height (uint32_t h)
862 bool height_changed = (height == 0) || (h != height);
863 gm.get_level_meter().setup_meters (gmlen);
865 TimeAxisView::set_height (h);
868 _view->set_height ((double) current_height());
871 if (height >= preset_height (HeightNormal)) {
875 gm.get_gain_slider().show();
877 if (!_route || _route->is_monitor()) {
882 if (rec_enable_button)
883 rec_enable_button->show();
885 route_group_button.show();
886 automation_button.show();
888 if (is_track() && track()->mode() == ARDOUR::Normal) {
889 playlist_button.show();
896 gm.get_gain_slider().hide();
898 if (!_route || _route->is_monitor()) {
903 if (rec_enable_button)
904 rec_enable_button->show();
906 route_group_button.hide ();
907 automation_button.hide ();
909 if (is_track() && track()->mode() == ARDOUR::Normal) {
910 playlist_button.hide ();
915 if (height_changed && !no_redraw) {
916 /* only emit the signal if the height really changed */
922 RouteTimeAxisView::route_color_changed ()
925 _view->apply_color (color(), StreamView::RegionColor);
930 RouteTimeAxisView::reset_samples_per_unit ()
932 set_samples_per_unit (_editor.get_current_zoom());
936 RouteTimeAxisView::horizontal_position_changed ()
939 _view->horizontal_position_changed ();
944 RouteTimeAxisView::set_samples_per_unit (double spu)
949 speed = track()->speed();
953 _view->set_samples_per_unit (spu * speed);
956 TimeAxisView::set_samples_per_unit (spu * speed);
960 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
962 if (!mitem->get_active()) {
963 /* this is one of the two calls made when these radio menu items change status. this one
964 is for the item that became inactive, and we want to ignore it.
969 if (apply_to_selection) {
970 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
973 track()->set_align_choice (choice);
979 RouteTimeAxisView::rename_current_playlist ()
981 ArdourPrompter prompter (true);
984 boost::shared_ptr<Track> tr = track();
985 if (!tr || tr->destructive()) {
989 boost::shared_ptr<Playlist> pl = tr->playlist();
994 prompter.set_title (_("Rename Playlist"));
995 prompter.set_prompt (_("New name for playlist:"));
996 prompter.set_initial_text (pl->name());
997 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
998 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1000 switch (prompter.run ()) {
1001 case Gtk::RESPONSE_ACCEPT:
1002 prompter.get_result (name);
1003 if (name.length()) {
1004 pl->set_name (name);
1014 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1016 std::string ret (basename);
1018 std::string const group_string = "." + route_group()->name() + ".";
1020 // iterate through all playlists
1022 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1023 std::string tmp = (*i)->name();
1025 std::string::size_type idx = tmp.find(group_string);
1026 // find those which belong to this group
1027 if (idx != string::npos) {
1028 tmp = tmp.substr(idx + group_string.length());
1030 // and find the largest current number
1031 int x = atoi(tmp.c_str());
1032 if (x > maxnumber) {
1041 snprintf (buf, sizeof(buf), "%d", maxnumber);
1043 ret = this->name() + "." + route_group()->name () + "." + buf;
1049 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1053 boost::shared_ptr<Track> tr = track ();
1054 if (!tr || tr->destructive()) {
1058 boost::shared_ptr<const Playlist> pl = tr->playlist();
1065 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1066 name = resolve_new_group_playlist_name(name, playlists_before_op);
1069 while (_session->playlists->by_name(name)) {
1070 name = Playlist::bump_name (name, *_session);
1073 // TODO: The prompter "new" button should be de-activated if the user
1074 // specifies a playlist name which already exists in the session.
1078 ArdourPrompter prompter (true);
1080 prompter.set_title (_("New Copy Playlist"));
1081 prompter.set_prompt (_("Name for new playlist:"));
1082 prompter.set_initial_text (name);
1083 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1084 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1085 prompter.show_all ();
1087 switch (prompter.run ()) {
1088 case Gtk::RESPONSE_ACCEPT:
1089 prompter.get_result (name);
1097 if (name.length()) {
1098 tr->use_copy_playlist ();
1099 tr->playlist()->set_name (name);
1104 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1108 boost::shared_ptr<Track> tr = track ();
1109 if (!tr || tr->destructive()) {
1113 boost::shared_ptr<const Playlist> pl = tr->playlist();
1120 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1121 name = resolve_new_group_playlist_name(name,playlists_before_op);
1124 while (_session->playlists->by_name(name)) {
1125 name = Playlist::bump_name (name, *_session);
1131 ArdourPrompter prompter (true);
1133 prompter.set_title (_("New Playlist"));
1134 prompter.set_prompt (_("Name for new playlist:"));
1135 prompter.set_initial_text (name);
1136 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1137 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1139 switch (prompter.run ()) {
1140 case Gtk::RESPONSE_ACCEPT:
1141 prompter.get_result (name);
1149 if (name.length()) {
1150 tr->use_new_playlist ();
1151 tr->playlist()->set_name (name);
1156 RouteTimeAxisView::clear_playlist ()
1158 boost::shared_ptr<Track> tr = track ();
1159 if (!tr || tr->destructive()) {
1163 boost::shared_ptr<Playlist> pl = tr->playlist();
1168 _editor.clear_playlist (pl);
1172 RouteTimeAxisView::speed_changed ()
1174 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1178 RouteTimeAxisView::update_diskstream_display ()
1188 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1190 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1192 /* special case: select/deselect all tracks */
1193 if (_editor.get_selection().selected (this)) {
1194 _editor.get_selection().clear_tracks ();
1196 _editor.select_all_tracks ();
1202 switch (ArdourKeyboard::selection_type (ev->state)) {
1203 case Selection::Toggle:
1204 _editor.get_selection().toggle (this);
1207 case Selection::Set:
1208 _editor.get_selection().set (this);
1211 case Selection::Extend:
1212 _editor.extend_selection_to_track (*this);
1215 case Selection::Add:
1216 _editor.get_selection().add (this);
1222 RouteTimeAxisView::set_selected_points (PointSelection& points)
1224 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1225 (*i)->set_selected_points (points);
1230 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1233 _view->set_selected_regionviews (regions);
1237 /** Add the selectable things that we have to a list.
1238 * @param results List to add things to.
1241 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1246 speed = track()->speed();
1249 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1250 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1252 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1253 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1256 /* pick up visible automation tracks */
1258 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1259 if (!(*i)->hidden()) {
1260 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1266 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1269 _view->get_inverted_selectables (sel, results);
1272 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1273 if (!(*i)->hidden()) {
1274 (*i)->get_inverted_selectables (sel, results);
1282 RouteTimeAxisView::route_group () const
1284 return _route->route_group();
1288 RouteTimeAxisView::name() const
1290 return _route->name();
1293 boost::shared_ptr<Playlist>
1294 RouteTimeAxisView::playlist () const
1296 boost::shared_ptr<Track> tr;
1298 if ((tr = track()) != 0) {
1299 return tr->playlist();
1301 return boost::shared_ptr<Playlist> ();
1306 RouteTimeAxisView::name_entry_changed ()
1308 string x = name_entry.get_text ();
1310 if (x == _route->name()) {
1314 strip_whitespace_edges (x);
1316 if (x.length() == 0) {
1317 name_entry.set_text (_route->name());
1321 if (_session->route_name_internal (x)) {
1322 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1324 name_entry.grab_focus ();
1325 } else if (RouteUI::verify_new_route_name (x)) {
1326 _route->set_name (x);
1328 name_entry.grab_focus ();
1332 boost::shared_ptr<Region>
1333 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1335 boost::shared_ptr<Playlist> pl = playlist ();
1338 return pl->find_next_region (pos, point, dir);
1341 return boost::shared_ptr<Region> ();
1345 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1347 boost::shared_ptr<Playlist> pl = playlist ();
1350 return pl->find_next_region_boundary (pos, dir);
1357 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1359 boost::shared_ptr<Playlist> what_we_got;
1360 boost::shared_ptr<Track> tr = track ();
1361 boost::shared_ptr<Playlist> playlist;
1364 /* route is a bus, not a track */
1368 playlist = tr->playlist();
1370 TimeSelection time (selection.time);
1371 float const speed = tr->speed();
1372 if (speed != 1.0f) {
1373 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1374 (*i).start = session_frame_to_track_frame((*i).start, speed);
1375 (*i).end = session_frame_to_track_frame((*i).end, speed);
1379 playlist->clear_changes ();
1380 playlist->clear_owned_changes ();
1384 if (playlist->cut (time) != 0) {
1385 vector<Command*> cmds;
1386 playlist->rdiff (cmds);
1387 _session->add_commands (cmds);
1389 _session->add_command (new StatefulDiffCommand (playlist));
1394 if ((what_we_got = playlist->cut (time)) != 0) {
1395 _editor.get_cut_buffer().add (what_we_got);
1396 vector<Command*> cmds;
1397 playlist->rdiff (cmds);
1398 _session->add_commands (cmds);
1400 _session->add_command (new StatefulDiffCommand (playlist));
1404 if ((what_we_got = playlist->copy (time)) != 0) {
1405 _editor.get_cut_buffer().add (what_we_got);
1410 if ((what_we_got = playlist->cut (time)) != 0) {
1412 vector<Command*> cmds;
1413 playlist->rdiff (cmds);
1414 _session->add_commands (cmds);
1415 _session->add_command (new StatefulDiffCommand (playlist));
1416 what_we_got->release ();
1423 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1429 boost::shared_ptr<Playlist> pl = playlist ();
1430 PlaylistSelection::iterator p;
1432 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1434 if (p == selection.playlists.end()) {
1438 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1440 if (track()->speed() != 1.0f) {
1441 pos = session_frame_to_track_frame (pos, track()->speed());
1442 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1445 pl->clear_changes ();
1446 pl->paste (*p, pos, times);
1447 _session->add_command (new StatefulDiffCommand (pl));
1453 struct PlaylistSorter {
1454 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1455 return a->sort_id() < b->sort_id();
1460 RouteTimeAxisView::build_playlist_menu ()
1462 using namespace Menu_Helpers;
1468 delete playlist_action_menu;
1469 playlist_action_menu = new Menu;
1470 playlist_action_menu->set_name ("ArdourContextMenu");
1472 MenuList& playlist_items = playlist_action_menu->items();
1473 playlist_action_menu->set_name ("ArdourContextMenu");
1474 playlist_items.clear();
1476 RadioMenuItem::Group playlist_group;
1477 boost::shared_ptr<Track> tr = track ();
1479 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1481 /* sort the playlists */
1483 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1485 /* add the playlists to the menu */
1486 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1487 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1488 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1489 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1491 if (tr->playlist()->id() == (*i)->id()) {
1497 playlist_items.push_back (SeparatorElem());
1498 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1499 playlist_items.push_back (SeparatorElem());
1501 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1502 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1503 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1506 // Use a label which tells the user what is happening
1507 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1508 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1512 playlist_items.push_back (SeparatorElem());
1513 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1514 playlist_items.push_back (SeparatorElem());
1516 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1520 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1522 assert (is_track());
1524 // exit if we were triggered by deactivating the old playlist
1525 if (!item->get_active()) {
1529 boost::shared_ptr<Playlist> pl (wpl.lock());
1535 if (track()->playlist() == pl) {
1536 // exit when use_playlist is called by the creation of the playlist menu
1537 // or the playlist choice is unchanged
1541 track()->use_playlist (pl);
1543 RouteGroup* rg = route_group();
1545 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1546 std::string group_string = "." + rg->name() + ".";
1548 std::string take_name = pl->name();
1549 std::string::size_type idx = take_name.find(group_string);
1551 if (idx == std::string::npos)
1554 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1556 boost::shared_ptr<RouteList> rl (rg->route_list());
1558 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1559 if ((*i) == this->route()) {
1563 std::string playlist_name = (*i)->name()+group_string+take_name;
1565 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1570 if (track->freeze_state() == Track::Frozen) {
1571 /* Don't change playlists of frozen tracks */
1575 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1577 // No playlist for this track for this take yet, make it
1578 track->use_new_playlist();
1579 track->playlist()->set_name(playlist_name);
1581 track->use_playlist(ipl);
1588 RouteTimeAxisView::update_playlist_tip ()
1590 RouteGroup* rg = route_group ();
1591 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1592 string group_string = "." + rg->name() + ".";
1594 string take_name = track()->playlist()->name();
1595 string::size_type idx = take_name.find(group_string);
1597 if (idx != string::npos) {
1598 /* find the bit containing the take number / name */
1599 take_name = take_name.substr (idx + group_string.length());
1601 /* set the playlist button tooltip to the take name */
1602 ARDOUR_UI::instance()->set_tip (
1604 string_compose(_("Take: %1.%2"),
1605 Glib::Markup::escape_text(rg->name()),
1606 Glib::Markup::escape_text(take_name))
1613 /* set the playlist button tooltip to the playlist name */
1614 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1619 RouteTimeAxisView::show_playlist_selector ()
1621 _editor.playlist_selector().show_for (this);
1625 RouteTimeAxisView::map_frozen ()
1631 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1633 switch (track()->freeze_state()) {
1635 playlist_button.set_sensitive (false);
1636 rec_enable_button->set_sensitive (false);
1639 playlist_button.set_sensitive (true);
1640 rec_enable_button->set_sensitive (true);
1646 RouteTimeAxisView::color_handler ()
1648 //case cTimeStretchOutline:
1649 if (timestretch_rect) {
1650 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1652 //case cTimeStretchFill:
1653 if (timestretch_rect) {
1654 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1660 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1661 * Will add track if necessary.
1664 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1666 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1667 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1670 /* it doesn't exist yet, so we don't care about the button state: just add it */
1671 create_automation_child (param, true);
1674 bool yn = menu->get_active();
1675 bool changed = false;
1677 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1679 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1680 will have done that for us.
1683 if (changed && !no_redraw) {
1691 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1693 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1699 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1701 if (menu && !_hidden) {
1702 ignore_toggle = true;
1703 menu->set_active (false);
1704 ignore_toggle = false;
1707 if (_route && !no_redraw) {
1714 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1716 if (apply_to_selection) {
1717 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1721 /* Show our automation */
1723 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1724 i->second->set_marked_for_display (true);
1726 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1729 menu->set_active(true);
1734 /* Show processor automation */
1736 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1737 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1738 if ((*ii)->view == 0) {
1739 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1742 (*ii)->menu_item->set_active (true);
1755 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1757 if (apply_to_selection) {
1758 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1762 /* Show our automation */
1764 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1765 if (i->second->has_automation()) {
1766 i->second->set_marked_for_display (true);
1768 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1770 menu->set_active(true);
1775 /* Show processor automation */
1777 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1778 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1779 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1780 (*ii)->menu_item->set_active (true);
1792 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1794 if (apply_to_selection) {
1795 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1799 /* Hide our automation */
1801 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1802 i->second->set_marked_for_display (false);
1804 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1807 menu->set_active (false);
1811 /* Hide processor automation */
1813 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1814 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1815 (*ii)->menu_item->set_active (false);
1826 RouteTimeAxisView::region_view_added (RegionView* rv)
1828 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1829 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1830 boost::shared_ptr<AutomationTimeAxisView> atv;
1832 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1837 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1838 (*i)->add_ghost(rv);
1842 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1844 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1850 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1852 parent.remove_processor_automation_node (this);
1856 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1859 remove_child (pan->view);
1863 RouteTimeAxisView::ProcessorAutomationNode*
1864 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1866 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1868 if ((*i)->processor == processor) {
1870 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1871 if ((*ii)->what == what) {
1881 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1883 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1886 ProcessorAutomationNode* pan;
1888 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1889 /* session state may never have been saved with new plugin */
1890 error << _("programming error: ")
1891 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1892 processor->name(), what.type(), (int) what.channel(), what.id() )
1902 boost::shared_ptr<AutomationControl> control
1903 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1905 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1906 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1907 _editor, *this, false, parent_canvas,
1908 processor->describe_parameter (what), processor->name()));
1910 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1912 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1915 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1920 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1923 pan->menu_item->set_active (false);
1932 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1934 boost::shared_ptr<Processor> processor (p.lock ());
1936 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1937 /* The Amp processor is a special case and is dealt with separately */
1941 set<Evoral::Parameter> existing;
1943 processor->what_has_data (existing);
1945 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1947 Evoral::Parameter param (*i);
1948 boost::shared_ptr<AutomationLine> al;
1950 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1953 add_processor_automation_curve (processor, param);
1959 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1961 using namespace Menu_Helpers;
1965 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1967 _automation_tracks[param] = track;
1969 /* existing state overrides "show" argument */
1970 string s = track->gui_property ("visible");
1972 show = string_is_affirmative (s);
1975 /* this might or might not change the visibility status, so don't rely on it */
1976 track->set_marked_for_display (show);
1978 if (show && !no_redraw) {
1982 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1983 /* MIDI-related parameters are always in the menu, there's no
1984 reason to rebuild the menu just because we added a automation
1985 lane for one of them. But if we add a non-MIDI automation
1986 lane, then we need to invalidate the display menu.
1988 delete display_menu;
1994 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1996 boost::shared_ptr<Processor> processor (p.lock ());
1998 if (!processor || !processor->display_to_user ()) {
2002 /* we use this override to veto the Amp processor from the plugin menu,
2003 as its automation lane can be accessed using the special "Fader" menu
2007 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2011 using namespace Menu_Helpers;
2012 ProcessorAutomationInfo *rai;
2013 list<ProcessorAutomationInfo*>::iterator x;
2015 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2017 if (automatable.empty()) {
2021 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2022 if ((*x)->processor == processor) {
2027 if (x == processor_automation.end()) {
2029 rai = new ProcessorAutomationInfo (processor);
2030 processor_automation.push_back (rai);
2038 /* any older menu was deleted at the top of processors_changed()
2039 when we cleared the subplugin menu.
2042 rai->menu = manage (new Menu);
2043 MenuList& items = rai->menu->items();
2044 rai->menu->set_name ("ArdourContextMenu");
2048 std::set<Evoral::Parameter> has_visible_automation;
2049 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2051 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2053 ProcessorAutomationNode* pan;
2054 CheckMenuItem* mitem;
2056 string name = processor->describe_parameter (*i);
2058 items.push_back (CheckMenuElem (name));
2059 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2061 _subplugin_menu_map[*i] = mitem;
2063 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2064 mitem->set_active(true);
2067 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2071 pan = new ProcessorAutomationNode (*i, mitem, *this);
2073 rai->lines.push_back (pan);
2077 pan->menu_item = mitem;
2081 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2084 /* add the menu for this processor, because the subplugin
2085 menu is always cleared at the top of processors_changed().
2086 this is the result of some poor design in gtkmm and/or
2090 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2095 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2096 RouteTimeAxisView::ProcessorAutomationNode* pan)
2098 bool showit = pan->menu_item->get_active();
2099 bool redraw = false;
2101 if (pan->view == 0 && showit) {
2102 add_processor_automation_curve (rai->processor, pan->what);
2106 if (pan->view && pan->view->set_marked_for_display (showit)) {
2110 if (redraw && !no_redraw) {
2116 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2118 if (c.type == RouteProcessorChange::MeterPointChange) {
2119 /* nothing to do if only the meter point has changed */
2123 using namespace Menu_Helpers;
2125 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2126 (*i)->valid = false;
2129 setup_processor_menu_and_curves ();
2131 bool deleted_processor_automation = false;
2133 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2135 list<ProcessorAutomationInfo*>::iterator tmp;
2143 processor_automation.erase (i);
2144 deleted_processor_automation = true;
2151 if (deleted_processor_automation && !no_redraw) {
2156 boost::shared_ptr<AutomationLine>
2157 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2159 ProcessorAutomationNode* pan;
2161 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2167 return boost::shared_ptr<AutomationLine>();
2171 RouteTimeAxisView::reset_processor_automation_curves ()
2173 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2179 RouteTimeAxisView::update_rec_display ()
2181 RouteUI::update_rec_display ();
2182 name_entry.set_sensitive (!_route->record_enabled());
2186 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2188 if (_ignore_set_layer_display) {
2192 if (apply_to_selection) {
2193 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2197 _view->set_layer_display (d);
2200 set_gui_property (X_("layer-display"), enum_2_string (d));
2205 RouteTimeAxisView::layer_display () const
2208 return _view->layer_display ();
2211 /* we don't know, since we don't have a _view, so just return something */
2217 boost::shared_ptr<AutomationTimeAxisView>
2218 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2220 AutomationTracks::iterator i = _automation_tracks.find(param);
2221 if (i != _automation_tracks.end()) {
2224 return boost::shared_ptr<AutomationTimeAxisView>();
2229 RouteTimeAxisView::fast_update ()
2231 gm.get_level_meter().update_meters ();
2235 RouteTimeAxisView::hide_meter ()
2238 gm.get_level_meter().hide_meters ();
2242 RouteTimeAxisView::show_meter ()
2248 RouteTimeAxisView::reset_meter ()
2250 if (Config->get_show_track_meters()) {
2251 gm.get_level_meter().setup_meters (height-5);
2258 RouteTimeAxisView::clear_meter ()
2260 gm.get_level_meter().clear_meters ();
2264 RouteTimeAxisView::meter_changed ()
2266 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2271 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2277 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2279 using namespace Menu_Helpers;
2281 if (!_underlay_streams.empty()) {
2282 MenuList& parent_items = parent_menu->items();
2283 Menu* gs_menu = manage (new Menu);
2284 gs_menu->set_name ("ArdourContextMenu");
2285 MenuList& gs_items = gs_menu->items();
2287 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2289 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2290 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2291 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2297 RouteTimeAxisView::set_underlay_state()
2299 if (!underlay_xml_node) {
2303 XMLNodeList nlist = underlay_xml_node->children();
2304 XMLNodeConstIterator niter;
2305 XMLNode *child_node;
2307 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2308 child_node = *niter;
2310 if (child_node->name() != "Underlay") {
2314 XMLProperty* prop = child_node->property ("id");
2316 PBD::ID id (prop->value());
2318 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2321 add_underlay(v->view(), false);
2330 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2336 RouteTimeAxisView& other = v->trackview();
2338 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2339 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2340 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2344 _underlay_streams.push_back(v);
2345 other._underlay_mirrors.push_back(this);
2347 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2349 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2351 if (!underlay_xml_node) {
2352 underlay_xml_node = xml_node->add_child("Underlays");
2355 XMLNode* node = underlay_xml_node->add_child("Underlay");
2356 XMLProperty* prop = node->add_property("id");
2357 prop->set_value(v->trackview().route()->id().to_s());
2364 RouteTimeAxisView::remove_underlay (StreamView* v)
2370 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2371 RouteTimeAxisView& other = v->trackview();
2373 if (it != _underlay_streams.end()) {
2374 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2376 if (gm == other._underlay_mirrors.end()) {
2377 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2381 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2383 _underlay_streams.erase(it);
2384 other._underlay_mirrors.erase(gm);
2386 if (underlay_xml_node) {
2387 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2393 RouteTimeAxisView::set_button_names ()
2395 if (_route && _route->solo_safe()) {
2396 solo_button->remove ();
2397 if (solo_safe_pixbuf == 0) {
2398 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2400 solo_button->set_image (solo_safe_pixbuf);
2401 solo_button->set_text (string());
2403 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2404 if (Config->get_solo_control_is_listen_control()) {
2405 switch (Config->get_listen_position()) {
2406 case AfterFaderListen:
2407 solo_button->set_text (_("A"));
2408 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2410 case PreFaderListen:
2411 solo_button->set_text (_("P"));
2412 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2416 solo_button->set_text (_("s"));
2417 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2420 mute_button->set_text (_("m"));
2424 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2426 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2427 if (i != _main_automation_menu_map.end()) {
2431 i = _subplugin_menu_map.find (param);
2432 if (i != _subplugin_menu_map.end()) {
2440 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2442 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2444 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2448 gain_track.reset (new AutomationTimeAxisView (_session,
2449 _route, _route->amp(), c, param,
2454 _route->amp()->describe_parameter(param)));
2457 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2460 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2464 void add_region_to_list (RegionView* rv, RegionList* l)
2466 l->push_back (rv->region());
2470 RouteTimeAxisView::combine_regions ()
2472 /* as of may 2011, we do not offer uncombine for MIDI tracks
2475 if (!is_audio_track()) {
2483 RegionList selected_regions;
2484 boost::shared_ptr<Playlist> playlist = track()->playlist();
2486 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2488 if (selected_regions.size() < 2) {
2492 playlist->clear_changes ();
2493 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2495 _session->add_command (new StatefulDiffCommand (playlist));
2496 /* make the new region be selected */
2498 return _view->find_view (compound_region);
2502 RouteTimeAxisView::uncombine_regions ()
2504 /* as of may 2011, we do not offer uncombine for MIDI tracks
2506 if (!is_audio_track()) {
2514 RegionList selected_regions;
2515 boost::shared_ptr<Playlist> playlist = track()->playlist();
2517 /* have to grab selected regions first because the uncombine is going
2518 * to change that in the middle of the list traverse
2521 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2523 playlist->clear_changes ();
2525 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2526 playlist->uncombine (*i);
2529 _session->add_command (new StatefulDiffCommand (playlist));
2533 RouteTimeAxisView::state_id() const
2535 return string_compose ("rtav %1", _route->id().to_s());
2540 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2542 TimeAxisView::remove_child (c);
2544 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2546 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2547 if (i->second == a) {
2548 _automation_tracks.erase (i);