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 */
252 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
253 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
254 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
256 PropertyList* plist = new PropertyList();
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);
269 RouteTimeAxisView::~RouteTimeAxisView ()
271 CatchDeletion (this);
273 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
277 delete playlist_action_menu;
278 playlist_action_menu = 0;
283 _automation_tracks.clear ();
285 delete route_group_menu;
289 RouteTimeAxisView::post_construct ()
291 /* map current state of the route */
293 update_diskstream_display ();
294 setup_processor_menu_and_curves ();
295 reset_processor_automation_curves ();
298 /** Set up the processor menu for the current set of processors, and
299 * display automation curves for any parameters which have data.
302 RouteTimeAxisView::setup_processor_menu_and_curves ()
304 _subplugin_menu_map.clear ();
305 subplugin_menu.items().clear ();
306 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
307 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
311 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
313 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
314 if (_route->route_group()) {
315 _route->route_group()->remove (_route);
321 r.push_back (route ());
323 route_group_menu->build (r);
324 route_group_menu->menu()->popup (ev->button, ev->time);
330 RouteTimeAxisView::playlist_changed ()
336 RouteTimeAxisView::label_view ()
338 string x = _route->name();
340 if (x != name_entry.get_text()) {
341 name_entry.set_text (x);
344 if (x != name_label.get_text()) {
345 name_label.set_text (x);
348 ARDOUR_UI::instance()->set_tip (name_entry, Glib::Markup::escape_text(x));
352 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
354 if (what_changed.contains (ARDOUR::Properties::name)) {
360 RouteTimeAxisView::take_name_changed (void *src)
368 RouteTimeAxisView::playlist_click ()
370 build_playlist_menu ();
371 conditionally_add_to_selection ();
372 playlist_action_menu->popup (1, gtk_get_current_event_time());
376 RouteTimeAxisView::automation_click ()
378 conditionally_add_to_selection ();
379 build_automation_action_menu (false);
380 automation_action_menu->popup (1, gtk_get_current_event_time());
384 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
386 using namespace Menu_Helpers;
388 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
389 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
392 detach_menu (subplugin_menu);
394 _main_automation_menu_map.clear ();
395 delete automation_action_menu;
396 automation_action_menu = new Menu;
398 MenuList& items = automation_action_menu->items();
400 automation_action_menu->set_name ("ArdourContextMenu");
402 items.push_back (MenuElem (_("Show All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
405 items.push_back (MenuElem (_("Show Existing Automation"),
406 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
408 items.push_back (MenuElem (_("Hide All Automation"),
409 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
411 items.push_back (SeparatorElem ());
413 /* Attach the plugin submenu. It may have previously been used elsewhere,
414 so it was detached above
417 if (!subplugin_menu.items().empty()) {
418 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
419 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
424 RouteTimeAxisView::build_display_menu ()
426 using namespace Menu_Helpers;
430 TimeAxisView::build_display_menu ();
432 /* now fill it with our stuff */
434 MenuList& items = display_menu->items();
435 display_menu->set_name ("ArdourContextMenu");
437 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
440 detach_menu (*_size_menu);
443 items.push_back (MenuElem (_("Height"), *_size_menu));
445 items.push_back (SeparatorElem());
447 if (!Profile->get_sae()) {
448 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
449 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
450 items.push_back (SeparatorElem());
453 // Hook for derived classes to add type specific stuff
454 append_extra_display_menu_items ();
458 Menu* layers_menu = manage (new Menu);
459 MenuList &layers_items = layers_menu->items();
460 layers_menu->set_name("ArdourContextMenu");
462 RadioMenuItem::Group layers_group;
464 /* Find out how many overlaid/stacked tracks we have in the selection */
468 TrackSelection const & s = _editor.get_selection().tracks;
469 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
470 StreamView* v = (*i)->view ();
475 switch (v->layer_display ()) {
486 /* We're not connecting to signal_toggled() here; in the case where these two items are
487 set to be in the `inconsistent' state, it seems that one or other will end up active
488 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
489 select the active one, no toggled signal is emitted so nothing happens.
492 _ignore_set_layer_display = true;
494 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
495 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
496 i->set_active (overlaid != 0 && stacked == 0);
497 i->set_inconsistent (overlaid != 0 && stacked != 0);
498 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
500 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
501 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
502 i->set_active (overlaid == 0 && stacked != 0);
503 i->set_inconsistent (overlaid != 0 && stacked != 0);
504 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
506 _ignore_set_layer_display = false;
508 items.push_back (MenuElem (_("Layers"), *layers_menu));
510 if (!Profile->get_sae()) {
512 Menu* alignment_menu = manage (new Menu);
513 MenuList& alignment_items = alignment_menu->items();
514 alignment_menu->set_name ("ArdourContextMenu");
516 RadioMenuItem::Group align_group;
518 /* Same verbose hacks as for the layering options above */
524 boost::shared_ptr<Track> first_track;
526 TrackSelection const & s = _editor.get_selection().tracks;
527 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
528 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
529 if (!r || !r->is_track ()) {
534 first_track = r->track();
537 switch (r->track()->alignment_choice()) {
541 switch (r->track()->alignment_style()) {
542 case ExistingMaterial:
550 case UseExistingMaterial:
566 inconsistent = false;
575 if (!inconsistent && first_track) {
577 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
578 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
579 i->set_active (automatic != 0 && existing == 0 && capture == 0);
580 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
582 switch (first_track->alignment_choice()) {
584 switch (first_track->alignment_style()) {
585 case ExistingMaterial:
586 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
589 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
597 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
598 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
599 i->set_active (existing != 0 && capture == 0 && automatic == 0);
600 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
602 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
603 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
604 i->set_active (existing == 0 && capture != 0 && automatic == 0);
605 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
607 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
613 Menu* mode_menu = manage (new Menu);
614 MenuList& mode_items = mode_menu->items ();
615 mode_menu->set_name ("ArdourContextMenu");
617 RadioMenuItem::Group mode_group;
623 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
624 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
625 if (!r || !r->is_track ()) {
629 switch (r->track()->mode()) {
642 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
643 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
644 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
645 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
646 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
648 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
649 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
650 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
651 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
652 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
654 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
655 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
656 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
657 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
658 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
660 items.push_back (MenuElem (_("Mode"), *mode_menu));
663 color_mode_menu = build_color_mode_menu();
664 if (color_mode_menu) {
665 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
668 items.push_back (SeparatorElem());
670 build_playlist_menu ();
671 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
672 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
675 route_group_menu->detach ();
678 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
679 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
681 r.push_back (rtv->route ());
686 r.push_back (route ());
689 route_group_menu->build (r);
690 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
692 build_automation_action_menu (true);
693 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
695 items.push_back (SeparatorElem());
699 TrackSelection const & s = _editor.get_selection().tracks;
700 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
701 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
706 if (r->route()->active()) {
713 items.push_back (CheckMenuElem (_("Active")));
714 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
715 bool click_sets_active = true;
716 if (active > 0 && inactive == 0) {
717 i->set_active (true);
718 click_sets_active = false;
719 } else if (active > 0 && inactive > 0) {
720 i->set_inconsistent (true);
722 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
724 items.push_back (SeparatorElem());
725 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
726 if (!Profile->get_sae()) {
727 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
729 items.push_front (SeparatorElem());
730 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
735 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
737 if (apply_to_selection) {
738 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
743 if (!track()->can_use_mode (mode, needs_bounce)) {
749 cerr << "would bounce this one\n";
754 track()->set_mode (mode);
756 rec_enable_button->remove ();
759 case ARDOUR::NonLayered:
761 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
762 rec_enable_button->set_text (string());
764 case ARDOUR::Destructive:
765 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
766 rec_enable_button->set_text (string());
770 rec_enable_button->show_all ();
775 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
777 TimeAxisView::show_timestretch (start, end, layers, layer);
787 /* check that the time selection was made in our route, or our route group.
788 remember that route_group() == 0 implies the route is *not* in a edit group.
791 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
792 /* this doesn't apply to us */
796 /* ignore it if our edit group is not active */
798 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
803 if (timestretch_rect == 0) {
804 timestretch_rect = new SimpleRect (*canvas_display ());
805 timestretch_rect->property_x1() = 0.0;
806 timestretch_rect->property_y1() = 0.0;
807 timestretch_rect->property_x2() = 0.0;
808 timestretch_rect->property_y2() = 0.0;
809 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
810 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
813 timestretch_rect->show ();
814 timestretch_rect->raise_to_top ();
816 double const x1 = start / _editor.get_current_zoom();
817 double const x2 = (end - 1) / _editor.get_current_zoom();
819 timestretch_rect->property_x1() = x1;
820 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
821 timestretch_rect->property_x2() = x2;
822 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
826 RouteTimeAxisView::hide_timestretch ()
828 TimeAxisView::hide_timestretch ();
830 if (timestretch_rect) {
831 timestretch_rect->hide ();
836 RouteTimeAxisView::show_selection (TimeSelection& ts)
840 /* ignore it if our edit group is not active or if the selection was started
841 in some other track or route group (remember that route_group() == 0 means
842 that the track is not in an route group).
845 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
846 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
852 TimeAxisView::show_selection (ts);
856 RouteTimeAxisView::set_height (uint32_t h)
859 bool height_changed = (height == 0) || (h != height);
860 gm.get_level_meter().setup_meters (gmlen);
862 TimeAxisView::set_height (h);
865 _view->set_height ((double) current_height());
868 if (height >= preset_height (HeightNormal)) {
872 gm.get_gain_slider().show();
874 if (!_route || _route->is_monitor()) {
879 if (rec_enable_button)
880 rec_enable_button->show();
882 route_group_button.show();
883 automation_button.show();
885 if (is_track() && track()->mode() == ARDOUR::Normal) {
886 playlist_button.show();
893 gm.get_gain_slider().hide();
895 if (!_route || _route->is_monitor()) {
900 if (rec_enable_button)
901 rec_enable_button->show();
903 route_group_button.hide ();
904 automation_button.hide ();
906 if (is_track() && track()->mode() == ARDOUR::Normal) {
907 playlist_button.hide ();
912 if (height_changed && !no_redraw) {
913 /* only emit the signal if the height really changed */
919 RouteTimeAxisView::route_color_changed ()
922 _view->apply_color (color(), StreamView::RegionColor);
927 RouteTimeAxisView::reset_samples_per_unit ()
929 set_samples_per_unit (_editor.get_current_zoom());
933 RouteTimeAxisView::horizontal_position_changed ()
936 _view->horizontal_position_changed ();
941 RouteTimeAxisView::set_samples_per_unit (double spu)
946 speed = track()->speed();
950 _view->set_samples_per_unit (spu * speed);
953 TimeAxisView::set_samples_per_unit (spu * speed);
957 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
959 if (!mitem->get_active()) {
960 /* this is one of the two calls made when these radio menu items change status. this one
961 is for the item that became inactive, and we want to ignore it.
966 if (apply_to_selection) {
967 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
970 track()->set_align_choice (choice);
976 RouteTimeAxisView::rename_current_playlist ()
978 ArdourPrompter prompter (true);
981 boost::shared_ptr<Track> tr = track();
982 if (!tr || tr->destructive()) {
986 boost::shared_ptr<Playlist> pl = tr->playlist();
991 prompter.set_title (_("Rename Playlist"));
992 prompter.set_prompt (_("New name for playlist:"));
993 prompter.set_initial_text (pl->name());
994 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
995 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
997 switch (prompter.run ()) {
998 case Gtk::RESPONSE_ACCEPT:
999 prompter.get_result (name);
1000 if (name.length()) {
1001 pl->set_name (name);
1011 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1013 std::string ret (basename);
1015 std::string const group_string = "." + route_group()->name() + ".";
1017 // iterate through all playlists
1019 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1020 std::string tmp = (*i)->name();
1022 std::string::size_type idx = tmp.find(group_string);
1023 // find those which belong to this group
1024 if (idx != string::npos) {
1025 tmp = tmp.substr(idx + group_string.length());
1027 // and find the largest current number
1028 int x = atoi(tmp.c_str());
1029 if (x > maxnumber) {
1038 snprintf (buf, sizeof(buf), "%d", maxnumber);
1040 ret = this->name() + "." + route_group()->name () + "." + buf;
1046 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1050 boost::shared_ptr<Track> tr = track ();
1051 if (!tr || tr->destructive()) {
1055 boost::shared_ptr<const Playlist> pl = tr->playlist();
1062 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1063 name = resolve_new_group_playlist_name(name, playlists_before_op);
1066 while (_session->playlists->by_name(name)) {
1067 name = Playlist::bump_name (name, *_session);
1070 // TODO: The prompter "new" button should be de-activated if the user
1071 // specifies a playlist name which already exists in the session.
1075 ArdourPrompter prompter (true);
1077 prompter.set_title (_("New Copy Playlist"));
1078 prompter.set_prompt (_("Name for new playlist:"));
1079 prompter.set_initial_text (name);
1080 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1081 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1082 prompter.show_all ();
1084 switch (prompter.run ()) {
1085 case Gtk::RESPONSE_ACCEPT:
1086 prompter.get_result (name);
1094 if (name.length()) {
1095 tr->use_copy_playlist ();
1096 tr->playlist()->set_name (name);
1101 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1105 boost::shared_ptr<Track> tr = track ();
1106 if (!tr || tr->destructive()) {
1110 boost::shared_ptr<const Playlist> pl = tr->playlist();
1117 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1118 name = resolve_new_group_playlist_name(name,playlists_before_op);
1121 while (_session->playlists->by_name(name)) {
1122 name = Playlist::bump_name (name, *_session);
1128 ArdourPrompter prompter (true);
1130 prompter.set_title (_("New Playlist"));
1131 prompter.set_prompt (_("Name for new playlist:"));
1132 prompter.set_initial_text (name);
1133 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1134 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1136 switch (prompter.run ()) {
1137 case Gtk::RESPONSE_ACCEPT:
1138 prompter.get_result (name);
1146 if (name.length()) {
1147 tr->use_new_playlist ();
1148 tr->playlist()->set_name (name);
1153 RouteTimeAxisView::clear_playlist ()
1155 boost::shared_ptr<Track> tr = track ();
1156 if (!tr || tr->destructive()) {
1160 boost::shared_ptr<Playlist> pl = tr->playlist();
1165 _editor.clear_playlist (pl);
1169 RouteTimeAxisView::speed_changed ()
1171 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1175 RouteTimeAxisView::update_diskstream_display ()
1185 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1187 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1189 /* special case: select/deselect all tracks */
1190 if (_editor.get_selection().selected (this)) {
1191 _editor.get_selection().clear_tracks ();
1193 _editor.select_all_tracks ();
1199 switch (ArdourKeyboard::selection_type (ev->state)) {
1200 case Selection::Toggle:
1201 _editor.get_selection().toggle (this);
1204 case Selection::Set:
1205 _editor.get_selection().set (this);
1208 case Selection::Extend:
1209 _editor.extend_selection_to_track (*this);
1212 case Selection::Add:
1213 _editor.get_selection().add (this);
1219 RouteTimeAxisView::set_selected_points (PointSelection& points)
1221 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1222 (*i)->set_selected_points (points);
1227 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1230 _view->set_selected_regionviews (regions);
1234 /** Add the selectable things that we have to a list.
1235 * @param results List to add things to.
1238 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1243 speed = track()->speed();
1246 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1247 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1249 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1250 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1253 /* pick up visible automation tracks */
1255 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1256 if (!(*i)->hidden()) {
1257 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1263 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1266 _view->get_inverted_selectables (sel, results);
1269 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1270 if (!(*i)->hidden()) {
1271 (*i)->get_inverted_selectables (sel, results);
1279 RouteTimeAxisView::route_group () const
1281 return _route->route_group();
1285 RouteTimeAxisView::name() const
1287 return _route->name();
1290 boost::shared_ptr<Playlist>
1291 RouteTimeAxisView::playlist () const
1293 boost::shared_ptr<Track> tr;
1295 if ((tr = track()) != 0) {
1296 return tr->playlist();
1298 return boost::shared_ptr<Playlist> ();
1303 RouteTimeAxisView::name_entry_changed ()
1305 string x = name_entry.get_text ();
1307 if (x == _route->name()) {
1311 strip_whitespace_edges (x);
1313 if (x.length() == 0) {
1314 name_entry.set_text (_route->name());
1318 if (_session->route_name_internal (x)) {
1319 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1321 name_entry.grab_focus ();
1322 } else if (RouteUI::verify_new_route_name (x)) {
1323 _route->set_name (x);
1325 name_entry.grab_focus ();
1329 boost::shared_ptr<Region>
1330 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1332 boost::shared_ptr<Playlist> pl = playlist ();
1335 return pl->find_next_region (pos, point, dir);
1338 return boost::shared_ptr<Region> ();
1342 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1344 boost::shared_ptr<Playlist> pl = playlist ();
1347 return pl->find_next_region_boundary (pos, dir);
1354 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1356 boost::shared_ptr<Playlist> what_we_got;
1357 boost::shared_ptr<Track> tr = track ();
1358 boost::shared_ptr<Playlist> playlist;
1361 /* route is a bus, not a track */
1365 playlist = tr->playlist();
1367 TimeSelection time (selection.time);
1368 float const speed = tr->speed();
1369 if (speed != 1.0f) {
1370 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1371 (*i).start = session_frame_to_track_frame((*i).start, speed);
1372 (*i).end = session_frame_to_track_frame((*i).end, speed);
1376 playlist->clear_changes ();
1377 playlist->clear_owned_changes ();
1381 if (playlist->cut (time) != 0) {
1382 vector<Command*> cmds;
1383 playlist->rdiff (cmds);
1384 _session->add_commands (cmds);
1386 _session->add_command (new StatefulDiffCommand (playlist));
1391 if ((what_we_got = playlist->cut (time)) != 0) {
1392 _editor.get_cut_buffer().add (what_we_got);
1393 vector<Command*> cmds;
1394 playlist->rdiff (cmds);
1395 _session->add_commands (cmds);
1397 _session->add_command (new StatefulDiffCommand (playlist));
1401 if ((what_we_got = playlist->copy (time)) != 0) {
1402 _editor.get_cut_buffer().add (what_we_got);
1407 if ((what_we_got = playlist->cut (time)) != 0) {
1409 vector<Command*> cmds;
1410 playlist->rdiff (cmds);
1411 _session->add_commands (cmds);
1412 _session->add_command (new StatefulDiffCommand (playlist));
1413 what_we_got->release ();
1420 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1426 boost::shared_ptr<Playlist> pl = playlist ();
1427 PlaylistSelection::iterator p;
1429 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1431 if (p == selection.playlists.end()) {
1435 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1437 if (track()->speed() != 1.0f) {
1438 pos = session_frame_to_track_frame (pos, track()->speed());
1439 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1442 pl->clear_changes ();
1443 pl->paste (*p, pos, times);
1444 _session->add_command (new StatefulDiffCommand (pl));
1450 struct PlaylistSorter {
1451 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1452 return a->sort_id() < b->sort_id();
1457 RouteTimeAxisView::build_playlist_menu ()
1459 using namespace Menu_Helpers;
1465 delete playlist_action_menu;
1466 playlist_action_menu = new Menu;
1467 playlist_action_menu->set_name ("ArdourContextMenu");
1469 MenuList& playlist_items = playlist_action_menu->items();
1470 playlist_action_menu->set_name ("ArdourContextMenu");
1471 playlist_items.clear();
1473 RadioMenuItem::Group playlist_group;
1474 boost::shared_ptr<Track> tr = track ();
1476 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1478 /* sort the playlists */
1480 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1482 /* add the playlists to the menu */
1483 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1484 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1485 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1486 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1488 if (tr->playlist()->id() == (*i)->id()) {
1494 playlist_items.push_back (SeparatorElem());
1495 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1496 playlist_items.push_back (SeparatorElem());
1498 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1499 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1500 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1503 // Use a label which tells the user what is happening
1504 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1505 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1509 playlist_items.push_back (SeparatorElem());
1510 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1511 playlist_items.push_back (SeparatorElem());
1513 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1517 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1519 assert (is_track());
1521 // exit if we were triggered by deactivating the old playlist
1522 if (!item->get_active()) {
1526 boost::shared_ptr<Playlist> pl (wpl.lock());
1532 if (track()->playlist() == pl) {
1533 // exit when use_playlist is called by the creation of the playlist menu
1534 // or the playlist choice is unchanged
1538 track()->use_playlist (pl);
1540 RouteGroup* rg = route_group();
1542 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1543 std::string group_string = "." + rg->name() + ".";
1545 std::string take_name = pl->name();
1546 std::string::size_type idx = take_name.find(group_string);
1548 if (idx == std::string::npos)
1551 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1553 boost::shared_ptr<RouteList> rl (rg->route_list());
1555 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1556 if ((*i) == this->route()) {
1560 std::string playlist_name = (*i)->name()+group_string+take_name;
1562 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1567 if (track->freeze_state() == Track::Frozen) {
1568 /* Don't change playlists of frozen tracks */
1572 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1574 // No playlist for this track for this take yet, make it
1575 track->use_new_playlist();
1576 track->playlist()->set_name(playlist_name);
1578 track->use_playlist(ipl);
1585 RouteTimeAxisView::update_playlist_tip ()
1587 RouteGroup* rg = route_group ();
1588 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1589 string group_string = "." + rg->name() + ".";
1591 string take_name = track()->playlist()->name();
1592 string::size_type idx = take_name.find(group_string);
1594 if (idx != string::npos) {
1595 /* find the bit containing the take number / name */
1596 take_name = take_name.substr (idx + group_string.length());
1598 /* set the playlist button tooltip to the take name */
1599 ARDOUR_UI::instance()->set_tip (
1601 string_compose(_("Take: %1.%2"),
1602 Glib::Markup::escape_text(rg->name()),
1603 Glib::Markup::escape_text(take_name))
1610 /* set the playlist button tooltip to the playlist name */
1611 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1616 RouteTimeAxisView::show_playlist_selector ()
1618 _editor.playlist_selector().show_for (this);
1622 RouteTimeAxisView::map_frozen ()
1628 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1630 switch (track()->freeze_state()) {
1632 playlist_button.set_sensitive (false);
1633 rec_enable_button->set_sensitive (false);
1636 playlist_button.set_sensitive (true);
1637 rec_enable_button->set_sensitive (true);
1643 RouteTimeAxisView::color_handler ()
1645 //case cTimeStretchOutline:
1646 if (timestretch_rect) {
1647 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1649 //case cTimeStretchFill:
1650 if (timestretch_rect) {
1651 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1657 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1658 * Will add track if necessary.
1661 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1663 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1664 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1667 /* it doesn't exist yet, so we don't care about the button state: just add it */
1668 create_automation_child (param, true);
1671 bool yn = menu->get_active();
1672 bool changed = false;
1674 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1676 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1677 will have done that for us.
1680 if (changed && !no_redraw) {
1688 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1690 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1696 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1698 if (menu && !_hidden) {
1699 ignore_toggle = true;
1700 menu->set_active (false);
1701 ignore_toggle = false;
1704 if (_route && !no_redraw) {
1711 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1713 if (apply_to_selection) {
1714 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1718 /* Show our automation */
1720 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1721 i->second->set_marked_for_display (true);
1723 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1726 menu->set_active(true);
1731 /* Show processor automation */
1733 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1734 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1735 if ((*ii)->view == 0) {
1736 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1739 (*ii)->menu_item->set_active (true);
1752 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1754 if (apply_to_selection) {
1755 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1759 /* Show our automation */
1761 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1762 if (i->second->has_automation()) {
1763 i->second->set_marked_for_display (true);
1765 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1767 menu->set_active(true);
1772 /* Show processor automation */
1774 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1775 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1776 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1777 (*ii)->menu_item->set_active (true);
1789 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1791 if (apply_to_selection) {
1792 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1796 /* Hide our automation */
1798 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1799 i->second->set_marked_for_display (false);
1801 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1804 menu->set_active (false);
1808 /* Hide processor automation */
1810 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1811 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1812 (*ii)->menu_item->set_active (false);
1823 RouteTimeAxisView::region_view_added (RegionView* rv)
1825 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1826 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1827 boost::shared_ptr<AutomationTimeAxisView> atv;
1829 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1834 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1835 (*i)->add_ghost(rv);
1839 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1841 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1847 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1849 parent.remove_processor_automation_node (this);
1853 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1856 remove_child (pan->view);
1860 RouteTimeAxisView::ProcessorAutomationNode*
1861 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1863 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1865 if ((*i)->processor == processor) {
1867 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1868 if ((*ii)->what == what) {
1878 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1880 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1883 ProcessorAutomationNode* pan;
1885 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1886 /* session state may never have been saved with new plugin */
1887 error << _("programming error: ")
1888 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1889 processor->name(), what.type(), (int) what.channel(), what.id() )
1899 boost::shared_ptr<AutomationControl> control
1900 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1902 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1903 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1904 _editor, *this, false, parent_canvas,
1905 processor->describe_parameter (what), processor->name()));
1907 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1909 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1912 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1917 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1920 pan->menu_item->set_active (false);
1929 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1931 boost::shared_ptr<Processor> processor (p.lock ());
1933 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1934 /* The Amp processor is a special case and is dealt with separately */
1938 set<Evoral::Parameter> existing;
1940 processor->what_has_data (existing);
1942 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1944 Evoral::Parameter param (*i);
1945 boost::shared_ptr<AutomationLine> al;
1947 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1950 add_processor_automation_curve (processor, param);
1956 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1958 using namespace Menu_Helpers;
1962 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1964 _automation_tracks[param] = track;
1966 /* existing state overrides "show" argument */
1967 string s = track->gui_property ("visible");
1969 show = string_is_affirmative (s);
1972 /* this might or might not change the visibility status, so don't rely on it */
1973 track->set_marked_for_display (show);
1975 if (show && !no_redraw) {
1979 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1980 /* MIDI-related parameters are always in the menu, there's no
1981 reason to rebuild the menu just because we added a automation
1982 lane for one of them. But if we add a non-MIDI automation
1983 lane, then we need to invalidate the display menu.
1985 delete display_menu;
1991 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1993 boost::shared_ptr<Processor> processor (p.lock ());
1995 if (!processor || !processor->display_to_user ()) {
1999 /* we use this override to veto the Amp processor from the plugin menu,
2000 as its automation lane can be accessed using the special "Fader" menu
2004 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2008 using namespace Menu_Helpers;
2009 ProcessorAutomationInfo *rai;
2010 list<ProcessorAutomationInfo*>::iterator x;
2012 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2014 if (automatable.empty()) {
2018 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2019 if ((*x)->processor == processor) {
2024 if (x == processor_automation.end()) {
2026 rai = new ProcessorAutomationInfo (processor);
2027 processor_automation.push_back (rai);
2035 /* any older menu was deleted at the top of processors_changed()
2036 when we cleared the subplugin menu.
2039 rai->menu = manage (new Menu);
2040 MenuList& items = rai->menu->items();
2041 rai->menu->set_name ("ArdourContextMenu");
2045 std::set<Evoral::Parameter> has_visible_automation;
2046 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2048 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2050 ProcessorAutomationNode* pan;
2051 CheckMenuItem* mitem;
2053 string name = processor->describe_parameter (*i);
2055 items.push_back (CheckMenuElem (name));
2056 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2058 _subplugin_menu_map[*i] = mitem;
2060 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2061 mitem->set_active(true);
2064 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2068 pan = new ProcessorAutomationNode (*i, mitem, *this);
2070 rai->lines.push_back (pan);
2074 pan->menu_item = mitem;
2078 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2081 /* add the menu for this processor, because the subplugin
2082 menu is always cleared at the top of processors_changed().
2083 this is the result of some poor design in gtkmm and/or
2087 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2092 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2093 RouteTimeAxisView::ProcessorAutomationNode* pan)
2095 bool showit = pan->menu_item->get_active();
2096 bool redraw = false;
2098 if (pan->view == 0 && showit) {
2099 add_processor_automation_curve (rai->processor, pan->what);
2103 if (pan->view && pan->view->set_marked_for_display (showit)) {
2107 if (redraw && !no_redraw) {
2113 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2115 if (c.type == RouteProcessorChange::MeterPointChange) {
2116 /* nothing to do if only the meter point has changed */
2120 using namespace Menu_Helpers;
2122 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2123 (*i)->valid = false;
2126 setup_processor_menu_and_curves ();
2128 bool deleted_processor_automation = false;
2130 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2132 list<ProcessorAutomationInfo*>::iterator tmp;
2140 processor_automation.erase (i);
2141 deleted_processor_automation = true;
2148 if (deleted_processor_automation && !no_redraw) {
2153 boost::shared_ptr<AutomationLine>
2154 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2156 ProcessorAutomationNode* pan;
2158 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2164 return boost::shared_ptr<AutomationLine>();
2168 RouteTimeAxisView::reset_processor_automation_curves ()
2170 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2176 RouteTimeAxisView::can_edit_name () const
2178 /* we do not allow track name changes if it is record enabled
2180 return !_route->record_enabled();
2184 RouteTimeAxisView::update_rec_display ()
2186 RouteUI::update_rec_display ();
2188 if (_route->record_enabled()) {
2198 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2200 if (_ignore_set_layer_display) {
2204 if (apply_to_selection) {
2205 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2209 _view->set_layer_display (d);
2212 set_gui_property (X_("layer-display"), enum_2_string (d));
2217 RouteTimeAxisView::layer_display () const
2220 return _view->layer_display ();
2223 /* we don't know, since we don't have a _view, so just return something */
2229 boost::shared_ptr<AutomationTimeAxisView>
2230 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2232 AutomationTracks::iterator i = _automation_tracks.find(param);
2233 if (i != _automation_tracks.end()) {
2236 return boost::shared_ptr<AutomationTimeAxisView>();
2241 RouteTimeAxisView::fast_update ()
2243 gm.get_level_meter().update_meters ();
2247 RouteTimeAxisView::hide_meter ()
2250 gm.get_level_meter().hide_meters ();
2254 RouteTimeAxisView::show_meter ()
2260 RouteTimeAxisView::reset_meter ()
2262 if (Config->get_show_track_meters()) {
2263 gm.get_level_meter().setup_meters (height-5);
2270 RouteTimeAxisView::clear_meter ()
2272 gm.get_level_meter().clear_meters ();
2276 RouteTimeAxisView::meter_changed ()
2278 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2283 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2289 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2291 using namespace Menu_Helpers;
2293 if (!_underlay_streams.empty()) {
2294 MenuList& parent_items = parent_menu->items();
2295 Menu* gs_menu = manage (new Menu);
2296 gs_menu->set_name ("ArdourContextMenu");
2297 MenuList& gs_items = gs_menu->items();
2299 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2301 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2302 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2303 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2309 RouteTimeAxisView::set_underlay_state()
2311 if (!underlay_xml_node) {
2315 XMLNodeList nlist = underlay_xml_node->children();
2316 XMLNodeConstIterator niter;
2317 XMLNode *child_node;
2319 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2320 child_node = *niter;
2322 if (child_node->name() != "Underlay") {
2326 XMLProperty* prop = child_node->property ("id");
2328 PBD::ID id (prop->value());
2330 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2333 add_underlay(v->view(), false);
2342 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2348 RouteTimeAxisView& other = v->trackview();
2350 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2351 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2352 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2356 _underlay_streams.push_back(v);
2357 other._underlay_mirrors.push_back(this);
2359 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2361 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2363 if (!underlay_xml_node) {
2364 underlay_xml_node = xml_node->add_child("Underlays");
2367 XMLNode* node = underlay_xml_node->add_child("Underlay");
2368 XMLProperty* prop = node->add_property("id");
2369 prop->set_value(v->trackview().route()->id().to_s());
2376 RouteTimeAxisView::remove_underlay (StreamView* v)
2382 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2383 RouteTimeAxisView& other = v->trackview();
2385 if (it != _underlay_streams.end()) {
2386 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2388 if (gm == other._underlay_mirrors.end()) {
2389 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2393 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2395 _underlay_streams.erase(it);
2396 other._underlay_mirrors.erase(gm);
2398 if (underlay_xml_node) {
2399 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2405 RouteTimeAxisView::set_button_names ()
2407 if (_route && _route->solo_safe()) {
2408 solo_button->remove ();
2409 if (solo_safe_pixbuf == 0) {
2410 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2412 solo_button->set_image (solo_safe_pixbuf);
2413 solo_button->set_text (string());
2415 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2416 if (Config->get_solo_control_is_listen_control()) {
2417 switch (Config->get_listen_position()) {
2418 case AfterFaderListen:
2419 solo_button->set_text (_("A"));
2420 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2422 case PreFaderListen:
2423 solo_button->set_text (_("P"));
2424 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2428 solo_button->set_text (_("s"));
2429 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2432 mute_button->set_text (_("m"));
2436 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2438 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2439 if (i != _main_automation_menu_map.end()) {
2443 i = _subplugin_menu_map.find (param);
2444 if (i != _subplugin_menu_map.end()) {
2452 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2454 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2456 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2460 gain_track.reset (new AutomationTimeAxisView (_session,
2461 _route, _route->amp(), c, param,
2466 _route->amp()->describe_parameter(param)));
2469 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2472 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2476 void add_region_to_list (RegionView* rv, RegionList* l)
2478 l->push_back (rv->region());
2482 RouteTimeAxisView::combine_regions ()
2484 /* as of may 2011, we do not offer uncombine for MIDI tracks
2487 if (!is_audio_track()) {
2495 RegionList selected_regions;
2496 boost::shared_ptr<Playlist> playlist = track()->playlist();
2498 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2500 if (selected_regions.size() < 2) {
2504 playlist->clear_changes ();
2505 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2507 _session->add_command (new StatefulDiffCommand (playlist));
2508 /* make the new region be selected */
2510 return _view->find_view (compound_region);
2514 RouteTimeAxisView::uncombine_regions ()
2516 /* as of may 2011, we do not offer uncombine for MIDI tracks
2518 if (!is_audio_track()) {
2526 RegionList selected_regions;
2527 boost::shared_ptr<Playlist> playlist = track()->playlist();
2529 /* have to grab selected regions first because the uncombine is going
2530 * to change that in the middle of the list traverse
2533 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2535 playlist->clear_changes ();
2537 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2538 playlist->uncombine (*i);
2541 _session->add_command (new StatefulDiffCommand (playlist));
2545 RouteTimeAxisView::state_id() const
2547 return string_compose ("rtav %1", _route->id().to_s());
2552 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2554 TimeAxisView::remove_child (c);
2556 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2558 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2559 if (i->second == a) {
2560 _automation_tracks.erase (i);