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/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
53 #include "evoral/Parameter.hpp"
55 #include "ardour_ui.h"
56 #include "ardour_button.h"
58 #include "global_signals.h"
59 #include "route_time_axis.h"
60 #include "automation_time_axis.h"
61 #include "canvas_impl.h"
63 #include "gui_thread.h"
65 #include "playlist_selector.h"
66 #include "point_selection.h"
68 #include "public_editor.h"
69 #include "region_view.h"
70 #include "rgb_macros.h"
71 #include "selection.h"
72 #include "simplerect.h"
73 #include "streamview.h"
75 #include "route_group_menu.h"
77 #include "ardour/track.h"
81 using namespace ARDOUR;
83 using namespace Gtkmm2ext;
85 using namespace Editing;
89 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
92 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
94 , parent_canvas (canvas)
97 , route_group_button (_("g"))
98 , playlist_button (_("p"))
99 , automation_button (_("a"))
100 , automation_action_menu (0)
101 , plugins_submenu_item (0)
102 , route_group_menu (0)
103 , playlist_action_menu (0)
105 , color_mode_menu (0)
106 , gm (sess, true, 125, 18)
107 , _ignore_set_layer_display (false)
112 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
114 RouteUI::set_route (rt);
117 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
120 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
121 gm.get_level_meter().set_no_show_all();
122 gm.get_level_meter().setup_meters(50, meter_width);
123 gm.update_gain_sensitive ();
125 string str = gui_property ("height");
127 set_height (atoi (str));
129 set_height (preset_height (HeightNormal));
132 if (!_route->is_auditioner()) {
133 if (gui_property ("visible").empty()) {
134 set_gui_property ("visible", true);
137 set_gui_property ("visible", false);
141 update_solo_display ();
143 timestretch_rect = 0;
146 ignore_toggle = false;
148 route_group_button.set_name ("route button");
149 playlist_button.set_name ("route button");
150 automation_button.set_name ("route button");
152 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
153 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
154 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
160 switch (track()->mode()) {
162 case ARDOUR::NonLayered:
163 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
165 case ARDOUR::Destructive:
166 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
170 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
172 if (is_midi_track()) {
173 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
174 gm.set_fader_name ("MidiTrackFader");
176 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
177 gm.set_fader_name ("AudioTrackFader");
180 rec_enable_button->set_sensitive (_session->writable());
182 /* set playlist button tip to the current playlist, and make it update when it changes */
183 update_playlist_tip ();
184 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
187 gm.set_fader_name ("AudioBusFader");
190 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
191 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
192 controls_hbox.pack_start(*mtrbox, false, false, 4);
195 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
196 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
197 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
199 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
201 if (!_route->is_master()) {
202 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
205 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
208 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
209 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
210 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
212 if (is_midi_track()) {
213 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
215 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
220 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
222 if (is_track() && track()->mode() == ARDOUR::Normal) {
223 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
228 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
229 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
233 str = gui_property ("layer-display");
235 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
238 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
239 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
241 /* pick up the correct freeze state */
246 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
247 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
248 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
250 PropertyList* plist = new PropertyList();
252 plist->add (ARDOUR::Properties::mute, true);
253 plist->add (ARDOUR::Properties::solo, true);
255 route_group_menu = new RouteGroupMenu (_session, plist);
257 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
259 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
262 RouteTimeAxisView::~RouteTimeAxisView ()
264 CatchDeletion (this);
266 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
270 delete playlist_action_menu;
271 playlist_action_menu = 0;
276 _automation_tracks.clear ();
278 delete route_group_menu;
282 RouteTimeAxisView::post_construct ()
284 /* map current state of the route */
286 update_diskstream_display ();
287 setup_processor_menu_and_curves ();
288 reset_processor_automation_curves ();
291 /** Set up the processor menu for the current set of processors, and
292 * display automation curves for any parameters which have data.
295 RouteTimeAxisView::setup_processor_menu_and_curves ()
297 _subplugin_menu_map.clear ();
298 subplugin_menu.items().clear ();
299 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
300 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
304 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
306 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
307 if (_route->route_group()) {
308 _route->route_group()->remove (_route);
314 r.push_back (route ());
316 route_group_menu->build (r);
317 route_group_menu->menu()->popup (ev->button, ev->time);
323 RouteTimeAxisView::playlist_changed ()
329 RouteTimeAxisView::label_view ()
331 string x = _route->name();
333 if (x != name_label.get_text()) {
334 name_label.set_text (x);
340 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
342 if (what_changed.contains (ARDOUR::Properties::name)) {
348 RouteTimeAxisView::take_name_changed (void *src)
356 RouteTimeAxisView::playlist_click ()
358 build_playlist_menu ();
359 conditionally_add_to_selection ();
360 playlist_action_menu->popup (1, gtk_get_current_event_time());
364 RouteTimeAxisView::automation_click ()
366 conditionally_add_to_selection ();
367 build_automation_action_menu (false);
368 automation_action_menu->popup (1, gtk_get_current_event_time());
372 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
374 using namespace Menu_Helpers;
376 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
377 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
380 detach_menu (subplugin_menu);
382 _main_automation_menu_map.clear ();
383 delete automation_action_menu;
384 automation_action_menu = new Menu;
386 MenuList& items = automation_action_menu->items();
388 automation_action_menu->set_name ("ArdourContextMenu");
390 items.push_back (MenuElem (_("Show All Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
393 items.push_back (MenuElem (_("Show Existing Automation"),
394 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
396 items.push_back (MenuElem (_("Hide All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
399 /* Attach the plugin submenu. It may have previously been used elsewhere,
400 so it was detached above
403 if (!subplugin_menu.items().empty()) {
404 items.push_back (SeparatorElem ());
405 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
406 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
411 RouteTimeAxisView::build_display_menu ()
413 using namespace Menu_Helpers;
417 TimeAxisView::build_display_menu ();
419 /* now fill it with our stuff */
421 MenuList& items = display_menu->items();
422 display_menu->set_name ("ArdourContextMenu");
424 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
427 detach_menu (*_size_menu);
430 items.push_back (MenuElem (_("Height"), *_size_menu));
432 items.push_back (SeparatorElem());
434 if (!Profile->get_sae()) {
435 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
436 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
437 items.push_back (SeparatorElem());
440 // Hook for derived classes to add type specific stuff
441 append_extra_display_menu_items ();
445 Menu* layers_menu = manage (new Menu);
446 MenuList &layers_items = layers_menu->items();
447 layers_menu->set_name("ArdourContextMenu");
449 RadioMenuItem::Group layers_group;
451 /* Find out how many overlaid/stacked tracks we have in the selection */
455 TrackSelection const & s = _editor.get_selection().tracks;
456 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
457 StreamView* v = (*i)->view ();
462 switch (v->layer_display ()) {
473 /* We're not connecting to signal_toggled() here; in the case where these two items are
474 set to be in the `inconsistent' state, it seems that one or other will end up active
475 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
476 select the active one, no toggled signal is emitted so nothing happens.
479 _ignore_set_layer_display = true;
481 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
482 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
483 i->set_active (overlaid != 0 && stacked == 0);
484 i->set_inconsistent (overlaid != 0 && stacked != 0);
485 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
487 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
488 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
489 i->set_active (overlaid == 0 && stacked != 0);
490 i->set_inconsistent (overlaid != 0 && stacked != 0);
491 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
493 _ignore_set_layer_display = false;
495 items.push_back (MenuElem (_("Layers"), *layers_menu));
497 if (!Profile->get_sae()) {
499 Menu* alignment_menu = manage (new Menu);
500 MenuList& alignment_items = alignment_menu->items();
501 alignment_menu->set_name ("ArdourContextMenu");
503 RadioMenuItem::Group align_group;
505 /* Same verbose hacks as for the layering options above */
511 boost::shared_ptr<Track> first_track;
513 TrackSelection const & s = _editor.get_selection().tracks;
514 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
515 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
516 if (!r || !r->is_track ()) {
521 first_track = r->track();
524 switch (r->track()->alignment_choice()) {
528 switch (r->track()->alignment_style()) {
529 case ExistingMaterial:
537 case UseExistingMaterial:
553 inconsistent = false;
562 if (!inconsistent && first_track) {
564 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
565 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
566 i->set_active (automatic != 0 && existing == 0 && capture == 0);
567 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
569 switch (first_track->alignment_choice()) {
571 switch (first_track->alignment_style()) {
572 case ExistingMaterial:
573 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
576 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
584 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
585 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
586 i->set_active (existing != 0 && capture == 0 && automatic == 0);
587 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
589 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
590 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
591 i->set_active (existing == 0 && capture != 0 && automatic == 0);
592 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
594 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
600 Menu* mode_menu = manage (new Menu);
601 MenuList& mode_items = mode_menu->items ();
602 mode_menu->set_name ("ArdourContextMenu");
604 RadioMenuItem::Group mode_group;
610 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
611 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
612 if (!r || !r->is_track ()) {
616 switch (r->track()->mode()) {
629 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
630 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
631 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
632 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
633 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
635 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
636 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
637 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
638 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
639 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
641 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
642 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
643 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
644 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
645 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
647 items.push_back (MenuElem (_("Mode"), *mode_menu));
651 items.push_back (SeparatorElem());
653 build_playlist_menu ();
654 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
655 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
658 route_group_menu->detach ();
661 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
662 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
664 r.push_back (rtv->route ());
669 r.push_back (route ());
672 route_group_menu->build (r);
673 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
675 build_automation_action_menu (true);
676 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
678 items.push_back (SeparatorElem());
682 TrackSelection const & s = _editor.get_selection().tracks;
683 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
684 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
689 if (r->route()->active()) {
696 items.push_back (CheckMenuElem (_("Active")));
697 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
698 bool click_sets_active = true;
699 if (active > 0 && inactive == 0) {
700 i->set_active (true);
701 click_sets_active = false;
702 } else if (active > 0 && inactive > 0) {
703 i->set_inconsistent (true);
705 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
707 items.push_back (SeparatorElem());
708 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
709 if (!Profile->get_sae()) {
710 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
712 items.push_front (SeparatorElem());
713 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
718 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
720 if (apply_to_selection) {
721 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
726 if (!track()->can_use_mode (mode, needs_bounce)) {
732 cerr << "would bounce this one\n";
737 track()->set_mode (mode);
739 rec_enable_button->remove ();
742 case ARDOUR::NonLayered:
744 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
745 rec_enable_button->set_text (string());
747 case ARDOUR::Destructive:
748 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
749 rec_enable_button->set_text (string());
753 rec_enable_button->show_all ();
758 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
760 TimeAxisView::show_timestretch (start, end, layers, layer);
770 /* check that the time selection was made in our route, or our route group.
771 remember that route_group() == 0 implies the route is *not* in a edit group.
774 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
775 /* this doesn't apply to us */
779 /* ignore it if our edit group is not active */
781 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
786 if (timestretch_rect == 0) {
787 timestretch_rect = new SimpleRect (*canvas_display ());
788 timestretch_rect->property_x1() = 0.0;
789 timestretch_rect->property_y1() = 0.0;
790 timestretch_rect->property_x2() = 0.0;
791 timestretch_rect->property_y2() = 0.0;
792 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
793 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
796 timestretch_rect->show ();
797 timestretch_rect->raise_to_top ();
799 double const x1 = start / _editor.get_current_zoom();
800 double const x2 = (end - 1) / _editor.get_current_zoom();
802 timestretch_rect->property_x1() = x1;
803 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
804 timestretch_rect->property_x2() = x2;
805 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
809 RouteTimeAxisView::hide_timestretch ()
811 TimeAxisView::hide_timestretch ();
813 if (timestretch_rect) {
814 timestretch_rect->hide ();
819 RouteTimeAxisView::show_selection (TimeSelection& ts)
823 /* ignore it if our edit group is not active or if the selection was started
824 in some other track or route group (remember that route_group() == 0 means
825 that the track is not in an route group).
828 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
829 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
835 TimeAxisView::show_selection (ts);
839 RouteTimeAxisView::set_height (uint32_t h)
842 bool height_changed = (height == 0) || (h != height);
845 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
848 gm.get_level_meter().setup_meters (gmlen, meter_width);
850 TimeAxisView::set_height (h);
853 _view->set_height ((double) current_height());
856 if (height >= preset_height (HeightNormal)) {
860 gm.get_gain_slider().show();
862 if (!_route || _route->is_monitor()) {
867 if (rec_enable_button)
868 rec_enable_button->show();
870 route_group_button.show();
871 automation_button.show();
873 if (is_track() && track()->mode() == ARDOUR::Normal) {
874 playlist_button.show();
881 gm.get_gain_slider().hide();
883 if (!_route || _route->is_monitor()) {
888 if (rec_enable_button)
889 rec_enable_button->show();
891 route_group_button.hide ();
892 automation_button.hide ();
894 if (is_track() && track()->mode() == ARDOUR::Normal) {
895 playlist_button.hide ();
900 if (height_changed && !no_redraw) {
901 /* only emit the signal if the height really changed */
907 RouteTimeAxisView::route_color_changed ()
910 _view->apply_color (color(), StreamView::RegionColor);
915 RouteTimeAxisView::reset_samples_per_unit ()
917 set_samples_per_unit (_editor.get_current_zoom());
921 RouteTimeAxisView::horizontal_position_changed ()
924 _view->horizontal_position_changed ();
929 RouteTimeAxisView::set_samples_per_unit (double spu)
934 speed = track()->speed();
938 _view->set_samples_per_unit (spu * speed);
941 TimeAxisView::set_samples_per_unit (spu * speed);
945 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
947 if (!mitem->get_active()) {
948 /* this is one of the two calls made when these radio menu items change status. this one
949 is for the item that became inactive, and we want to ignore it.
954 if (apply_to_selection) {
955 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
958 track()->set_align_choice (choice);
964 RouteTimeAxisView::rename_current_playlist ()
966 ArdourPrompter prompter (true);
969 boost::shared_ptr<Track> tr = track();
970 if (!tr || tr->destructive()) {
974 boost::shared_ptr<Playlist> pl = tr->playlist();
979 prompter.set_title (_("Rename Playlist"));
980 prompter.set_prompt (_("New name for playlist:"));
981 prompter.set_initial_text (pl->name());
982 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
983 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
985 switch (prompter.run ()) {
986 case Gtk::RESPONSE_ACCEPT:
987 prompter.get_result (name);
999 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1001 std::string ret (basename);
1003 std::string const group_string = "." + route_group()->name() + ".";
1005 // iterate through all playlists
1007 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1008 std::string tmp = (*i)->name();
1010 std::string::size_type idx = tmp.find(group_string);
1011 // find those which belong to this group
1012 if (idx != string::npos) {
1013 tmp = tmp.substr(idx + group_string.length());
1015 // and find the largest current number
1017 if (x > maxnumber) {
1026 snprintf (buf, sizeof(buf), "%d", maxnumber);
1028 ret = this->name() + "." + route_group()->name () + "." + buf;
1034 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1038 boost::shared_ptr<Track> tr = track ();
1039 if (!tr || tr->destructive()) {
1043 boost::shared_ptr<const Playlist> pl = tr->playlist();
1050 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1051 name = resolve_new_group_playlist_name(name, playlists_before_op);
1054 while (_session->playlists->by_name(name)) {
1055 name = Playlist::bump_name (name, *_session);
1058 // TODO: The prompter "new" button should be de-activated if the user
1059 // specifies a playlist name which already exists in the session.
1063 ArdourPrompter prompter (true);
1065 prompter.set_title (_("New Copy Playlist"));
1066 prompter.set_prompt (_("Name for new playlist:"));
1067 prompter.set_initial_text (name);
1068 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1069 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1070 prompter.show_all ();
1072 switch (prompter.run ()) {
1073 case Gtk::RESPONSE_ACCEPT:
1074 prompter.get_result (name);
1082 if (name.length()) {
1083 tr->use_copy_playlist ();
1084 tr->playlist()->set_name (name);
1089 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1093 boost::shared_ptr<Track> tr = track ();
1094 if (!tr || tr->destructive()) {
1098 boost::shared_ptr<const Playlist> pl = tr->playlist();
1105 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1106 name = resolve_new_group_playlist_name(name,playlists_before_op);
1109 while (_session->playlists->by_name(name)) {
1110 name = Playlist::bump_name (name, *_session);
1116 ArdourPrompter prompter (true);
1118 prompter.set_title (_("New Playlist"));
1119 prompter.set_prompt (_("Name for new playlist:"));
1120 prompter.set_initial_text (name);
1121 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1122 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1124 switch (prompter.run ()) {
1125 case Gtk::RESPONSE_ACCEPT:
1126 prompter.get_result (name);
1134 if (name.length()) {
1135 tr->use_new_playlist ();
1136 tr->playlist()->set_name (name);
1141 RouteTimeAxisView::clear_playlist ()
1143 boost::shared_ptr<Track> tr = track ();
1144 if (!tr || tr->destructive()) {
1148 boost::shared_ptr<Playlist> pl = tr->playlist();
1153 _editor.clear_playlist (pl);
1157 RouteTimeAxisView::speed_changed ()
1159 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1163 RouteTimeAxisView::update_diskstream_display ()
1173 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1175 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1177 /* special case: select/deselect all tracks */
1178 if (_editor.get_selection().selected (this)) {
1179 _editor.get_selection().clear_tracks ();
1181 _editor.select_all_tracks ();
1187 switch (ArdourKeyboard::selection_type (ev->state)) {
1188 case Selection::Toggle:
1189 _editor.get_selection().toggle (this);
1192 case Selection::Set:
1193 _editor.get_selection().set (this);
1196 case Selection::Extend:
1197 _editor.extend_selection_to_track (*this);
1200 case Selection::Add:
1201 _editor.get_selection().add (this);
1207 RouteTimeAxisView::set_selected_points (PointSelection& points)
1209 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1210 (*i)->set_selected_points (points);
1215 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1218 _view->set_selected_regionviews (regions);
1222 /** Add the selectable things that we have to a list.
1223 * @param results List to add things to.
1226 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1231 speed = track()->speed();
1234 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1235 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1237 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1238 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1241 /* pick up visible automation tracks */
1243 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1244 if (!(*i)->hidden()) {
1245 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1251 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1254 _view->get_inverted_selectables (sel, results);
1257 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1258 if (!(*i)->hidden()) {
1259 (*i)->get_inverted_selectables (sel, results);
1267 RouteTimeAxisView::route_group () const
1269 return _route->route_group();
1273 RouteTimeAxisView::name() const
1275 return _route->name();
1278 boost::shared_ptr<Playlist>
1279 RouteTimeAxisView::playlist () const
1281 boost::shared_ptr<Track> tr;
1283 if ((tr = track()) != 0) {
1284 return tr->playlist();
1286 return boost::shared_ptr<Playlist> ();
1291 RouteTimeAxisView::name_entry_changed ()
1293 TimeAxisView::name_entry_changed ();
1295 string x = name_entry->get_text ();
1297 if (x == _route->name()) {
1301 strip_whitespace_edges (x);
1303 if (x.length() == 0) {
1304 name_entry->set_text (_route->name());
1308 if (_session->route_name_internal (x)) {
1309 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1311 name_entry->grab_focus ();
1312 } else if (RouteUI::verify_new_route_name (x)) {
1313 _route->set_name (x);
1315 name_entry->grab_focus ();
1319 boost::shared_ptr<Region>
1320 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1322 boost::shared_ptr<Playlist> pl = playlist ();
1325 return pl->find_next_region (pos, point, dir);
1328 return boost::shared_ptr<Region> ();
1332 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1334 boost::shared_ptr<Playlist> pl = playlist ();
1337 return pl->find_next_region_boundary (pos, dir);
1344 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1346 boost::shared_ptr<Playlist> what_we_got;
1347 boost::shared_ptr<Track> tr = track ();
1348 boost::shared_ptr<Playlist> playlist;
1351 /* route is a bus, not a track */
1355 playlist = tr->playlist();
1357 TimeSelection time (selection.time);
1358 float const speed = tr->speed();
1359 if (speed != 1.0f) {
1360 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1361 (*i).start = session_frame_to_track_frame((*i).start, speed);
1362 (*i).end = session_frame_to_track_frame((*i).end, speed);
1366 playlist->clear_changes ();
1367 playlist->clear_owned_changes ();
1371 if (playlist->cut (time) != 0) {
1372 vector<Command*> cmds;
1373 playlist->rdiff (cmds);
1374 _session->add_commands (cmds);
1376 _session->add_command (new StatefulDiffCommand (playlist));
1381 if ((what_we_got = playlist->cut (time)) != 0) {
1382 _editor.get_cut_buffer().add (what_we_got);
1383 vector<Command*> cmds;
1384 playlist->rdiff (cmds);
1385 _session->add_commands (cmds);
1387 _session->add_command (new StatefulDiffCommand (playlist));
1391 if ((what_we_got = playlist->copy (time)) != 0) {
1392 _editor.get_cut_buffer().add (what_we_got);
1397 if ((what_we_got = playlist->cut (time)) != 0) {
1399 vector<Command*> cmds;
1400 playlist->rdiff (cmds);
1401 _session->add_commands (cmds);
1402 _session->add_command (new StatefulDiffCommand (playlist));
1403 what_we_got->release ();
1410 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1416 boost::shared_ptr<Playlist> pl = playlist ();
1417 PlaylistSelection::iterator p;
1419 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1421 if (p == selection.playlists.end()) {
1425 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1427 if (track()->speed() != 1.0f) {
1428 pos = session_frame_to_track_frame (pos, track()->speed());
1429 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1432 pl->clear_changes ();
1433 pl->paste (*p, pos, times);
1434 _session->add_command (new StatefulDiffCommand (pl));
1440 struct PlaylistSorter {
1441 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1442 return a->sort_id() < b->sort_id();
1447 RouteTimeAxisView::build_playlist_menu ()
1449 using namespace Menu_Helpers;
1455 delete playlist_action_menu;
1456 playlist_action_menu = new Menu;
1457 playlist_action_menu->set_name ("ArdourContextMenu");
1459 MenuList& playlist_items = playlist_action_menu->items();
1460 playlist_action_menu->set_name ("ArdourContextMenu");
1461 playlist_items.clear();
1463 RadioMenuItem::Group playlist_group;
1464 boost::shared_ptr<Track> tr = track ();
1466 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1468 /* sort the playlists */
1470 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1472 /* add the playlists to the menu */
1473 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1474 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1475 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1476 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1478 if (tr->playlist()->id() == (*i)->id()) {
1484 playlist_items.push_back (SeparatorElem());
1485 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1486 playlist_items.push_back (SeparatorElem());
1488 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1489 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1490 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1493 // Use a label which tells the user what is happening
1494 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1495 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1499 playlist_items.push_back (SeparatorElem());
1500 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1501 playlist_items.push_back (SeparatorElem());
1503 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1507 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1509 assert (is_track());
1511 // exit if we were triggered by deactivating the old playlist
1512 if (!item->get_active()) {
1516 boost::shared_ptr<Playlist> pl (wpl.lock());
1522 if (track()->playlist() == pl) {
1523 // exit when use_playlist is called by the creation of the playlist menu
1524 // or the playlist choice is unchanged
1528 track()->use_playlist (pl);
1530 RouteGroup* rg = route_group();
1532 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1533 std::string group_string = "." + rg->name() + ".";
1535 std::string take_name = pl->name();
1536 std::string::size_type idx = take_name.find(group_string);
1538 if (idx == std::string::npos)
1541 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1543 boost::shared_ptr<RouteList> rl (rg->route_list());
1545 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1546 if ((*i) == this->route()) {
1550 std::string playlist_name = (*i)->name()+group_string+take_name;
1552 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1557 if (track->freeze_state() == Track::Frozen) {
1558 /* Don't change playlists of frozen tracks */
1562 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1564 // No playlist for this track for this take yet, make it
1565 track->use_new_playlist();
1566 track->playlist()->set_name(playlist_name);
1568 track->use_playlist(ipl);
1575 RouteTimeAxisView::update_playlist_tip ()
1577 RouteGroup* rg = route_group ();
1578 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1579 string group_string = "." + rg->name() + ".";
1581 string take_name = track()->playlist()->name();
1582 string::size_type idx = take_name.find(group_string);
1584 if (idx != string::npos) {
1585 /* find the bit containing the take number / name */
1586 take_name = take_name.substr (idx + group_string.length());
1588 /* set the playlist button tooltip to the take name */
1589 ARDOUR_UI::instance()->set_tip (
1591 string_compose(_("Take: %1.%2"),
1592 Glib::Markup::escape_text(rg->name()),
1593 Glib::Markup::escape_text(take_name))
1600 /* set the playlist button tooltip to the playlist name */
1601 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1606 RouteTimeAxisView::show_playlist_selector ()
1608 _editor.playlist_selector().show_for (this);
1612 RouteTimeAxisView::map_frozen ()
1618 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1620 switch (track()->freeze_state()) {
1622 playlist_button.set_sensitive (false);
1623 rec_enable_button->set_sensitive (false);
1626 playlist_button.set_sensitive (true);
1627 rec_enable_button->set_sensitive (true);
1633 RouteTimeAxisView::color_handler ()
1635 //case cTimeStretchOutline:
1636 if (timestretch_rect) {
1637 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1639 //case cTimeStretchFill:
1640 if (timestretch_rect) {
1641 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1647 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1648 * Will add track if necessary.
1651 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1653 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1654 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1657 /* it doesn't exist yet, so we don't care about the button state: just add it */
1658 create_automation_child (param, true);
1661 bool yn = menu->get_active();
1662 bool changed = false;
1664 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1666 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1667 will have done that for us.
1670 if (changed && !no_redraw) {
1678 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1680 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1686 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1688 if (menu && !_hidden) {
1689 ignore_toggle = true;
1690 menu->set_active (false);
1691 ignore_toggle = false;
1694 if (_route && !no_redraw) {
1701 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1703 if (apply_to_selection) {
1704 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1708 /* Show our automation */
1710 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1711 i->second->set_marked_for_display (true);
1713 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1716 menu->set_active(true);
1721 /* Show processor automation */
1723 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1724 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1725 if ((*ii)->view == 0) {
1726 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1729 (*ii)->menu_item->set_active (true);
1742 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1744 if (apply_to_selection) {
1745 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1749 /* Show our automation */
1751 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1752 if (i->second->has_automation()) {
1753 i->second->set_marked_for_display (true);
1755 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1757 menu->set_active(true);
1762 /* Show processor automation */
1764 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1765 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1766 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1767 (*ii)->menu_item->set_active (true);
1779 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1781 if (apply_to_selection) {
1782 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1786 /* Hide our automation */
1788 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1789 i->second->set_marked_for_display (false);
1791 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1794 menu->set_active (false);
1798 /* Hide processor automation */
1800 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1801 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1802 (*ii)->menu_item->set_active (false);
1813 RouteTimeAxisView::region_view_added (RegionView* rv)
1815 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1816 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1817 boost::shared_ptr<AutomationTimeAxisView> atv;
1819 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1824 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1825 (*i)->add_ghost(rv);
1829 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1831 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1837 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1839 parent.remove_processor_automation_node (this);
1843 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1846 remove_child (pan->view);
1850 RouteTimeAxisView::ProcessorAutomationNode*
1851 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1853 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1855 if ((*i)->processor == processor) {
1857 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1858 if ((*ii)->what == what) {
1868 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1870 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1873 ProcessorAutomationNode* pan;
1875 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1876 /* session state may never have been saved with new plugin */
1877 error << _("programming error: ")
1878 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1879 processor->name(), what.type(), (int) what.channel(), what.id() )
1889 boost::shared_ptr<AutomationControl> control
1890 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1892 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1893 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1894 _editor, *this, false, parent_canvas,
1895 processor->describe_parameter (what), processor->name()));
1897 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1899 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1902 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1907 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1910 pan->menu_item->set_active (false);
1919 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1921 boost::shared_ptr<Processor> processor (p.lock ());
1923 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1924 /* The Amp processor is a special case and is dealt with separately */
1928 set<Evoral::Parameter> existing;
1930 processor->what_has_data (existing);
1932 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1934 Evoral::Parameter param (*i);
1935 boost::shared_ptr<AutomationLine> al;
1937 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1940 add_processor_automation_curve (processor, param);
1946 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1948 using namespace Menu_Helpers;
1952 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1954 _automation_tracks[param] = track;
1956 /* existing state overrides "show" argument */
1957 string s = track->gui_property ("visible");
1959 show = string_is_affirmative (s);
1962 /* this might or might not change the visibility status, so don't rely on it */
1963 track->set_marked_for_display (show);
1965 if (show && !no_redraw) {
1969 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1970 /* MIDI-related parameters are always in the menu, there's no
1971 reason to rebuild the menu just because we added a automation
1972 lane for one of them. But if we add a non-MIDI automation
1973 lane, then we need to invalidate the display menu.
1975 delete display_menu;
1981 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1983 boost::shared_ptr<Processor> processor (p.lock ());
1985 if (!processor || !processor->display_to_user ()) {
1989 /* we use this override to veto the Amp processor from the plugin menu,
1990 as its automation lane can be accessed using the special "Fader" menu
1994 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1998 using namespace Menu_Helpers;
1999 ProcessorAutomationInfo *rai;
2000 list<ProcessorAutomationInfo*>::iterator x;
2002 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2004 if (automatable.empty()) {
2008 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2009 if ((*x)->processor == processor) {
2014 if (x == processor_automation.end()) {
2016 rai = new ProcessorAutomationInfo (processor);
2017 processor_automation.push_back (rai);
2025 /* any older menu was deleted at the top of processors_changed()
2026 when we cleared the subplugin menu.
2029 rai->menu = manage (new Menu);
2030 MenuList& items = rai->menu->items();
2031 rai->menu->set_name ("ArdourContextMenu");
2035 std::set<Evoral::Parameter> has_visible_automation;
2036 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2038 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2040 ProcessorAutomationNode* pan;
2041 Gtk::CheckMenuItem* mitem;
2043 string name = processor->describe_parameter (*i);
2045 items.push_back (CheckMenuElem (name));
2046 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2048 _subplugin_menu_map[*i] = mitem;
2050 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2051 mitem->set_active(true);
2054 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2058 pan = new ProcessorAutomationNode (*i, mitem, *this);
2060 rai->lines.push_back (pan);
2064 pan->menu_item = mitem;
2068 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2071 /* add the menu for this processor, because the subplugin
2072 menu is always cleared at the top of processors_changed().
2073 this is the result of some poor design in gtkmm and/or
2077 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2082 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2083 RouteTimeAxisView::ProcessorAutomationNode* pan)
2085 bool showit = pan->menu_item->get_active();
2086 bool redraw = false;
2088 if (pan->view == 0 && showit) {
2089 add_processor_automation_curve (rai->processor, pan->what);
2093 if (pan->view && pan->view->set_marked_for_display (showit)) {
2097 if (redraw && !no_redraw) {
2103 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2105 if (c.type == RouteProcessorChange::MeterPointChange) {
2106 /* nothing to do if only the meter point has changed */
2110 using namespace Menu_Helpers;
2112 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2113 (*i)->valid = false;
2116 setup_processor_menu_and_curves ();
2118 bool deleted_processor_automation = false;
2120 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2122 list<ProcessorAutomationInfo*>::iterator tmp;
2130 processor_automation.erase (i);
2131 deleted_processor_automation = true;
2138 if (deleted_processor_automation && !no_redraw) {
2143 boost::shared_ptr<AutomationLine>
2144 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2146 ProcessorAutomationNode* pan;
2148 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2154 return boost::shared_ptr<AutomationLine>();
2158 RouteTimeAxisView::reset_processor_automation_curves ()
2160 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2166 RouteTimeAxisView::can_edit_name () const
2168 /* we do not allow track name changes if it is record enabled
2170 return !_route->record_enabled();
2174 RouteTimeAxisView::update_rec_display ()
2176 RouteUI::update_rec_display ();
2180 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2182 if (_ignore_set_layer_display) {
2186 if (apply_to_selection) {
2187 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2191 _view->set_layer_display (d);
2194 set_gui_property (X_("layer-display"), enum_2_string (d));
2199 RouteTimeAxisView::layer_display () const
2202 return _view->layer_display ();
2205 /* we don't know, since we don't have a _view, so just return something */
2211 boost::shared_ptr<AutomationTimeAxisView>
2212 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2214 AutomationTracks::iterator i = _automation_tracks.find(param);
2215 if (i != _automation_tracks.end()) {
2218 return boost::shared_ptr<AutomationTimeAxisView>();
2223 RouteTimeAxisView::fast_update ()
2225 gm.get_level_meter().update_meters ();
2229 RouteTimeAxisView::hide_meter ()
2232 gm.get_level_meter().hide_meters ();
2236 RouteTimeAxisView::show_meter ()
2242 RouteTimeAxisView::reset_meter ()
2244 if (Config->get_show_track_meters()) {
2245 int meter_width = 3;
2246 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2249 gm.get_level_meter().setup_meters (height - 9, meter_width);
2256 RouteTimeAxisView::clear_meter ()
2258 gm.get_level_meter().clear_meters ();
2262 RouteTimeAxisView::meter_changed ()
2264 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2266 if (_route && !no_redraw) {
2272 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2275 if (_route && !no_redraw) {
2281 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2283 using namespace Menu_Helpers;
2285 if (!_underlay_streams.empty()) {
2286 MenuList& parent_items = parent_menu->items();
2287 Menu* gs_menu = manage (new Menu);
2288 gs_menu->set_name ("ArdourContextMenu");
2289 MenuList& gs_items = gs_menu->items();
2291 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2293 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2294 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2295 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2301 RouteTimeAxisView::set_underlay_state()
2303 if (!underlay_xml_node) {
2307 XMLNodeList nlist = underlay_xml_node->children();
2308 XMLNodeConstIterator niter;
2309 XMLNode *child_node;
2311 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2312 child_node = *niter;
2314 if (child_node->name() != "Underlay") {
2318 XMLProperty* prop = child_node->property ("id");
2320 PBD::ID id (prop->value());
2322 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2325 add_underlay(v->view(), false);
2334 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2340 RouteTimeAxisView& other = v->trackview();
2342 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2343 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2344 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2348 _underlay_streams.push_back(v);
2349 other._underlay_mirrors.push_back(this);
2351 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2353 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2355 if (!underlay_xml_node) {
2356 underlay_xml_node = xml_node->add_child("Underlays");
2359 XMLNode* node = underlay_xml_node->add_child("Underlay");
2360 XMLProperty* prop = node->add_property("id");
2361 prop->set_value(v->trackview().route()->id().to_s());
2368 RouteTimeAxisView::remove_underlay (StreamView* v)
2374 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2375 RouteTimeAxisView& other = v->trackview();
2377 if (it != _underlay_streams.end()) {
2378 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2380 if (gm == other._underlay_mirrors.end()) {
2381 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2385 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2387 _underlay_streams.erase(it);
2388 other._underlay_mirrors.erase(gm);
2390 if (underlay_xml_node) {
2391 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2397 RouteTimeAxisView::set_button_names ()
2399 if (_route && _route->solo_safe()) {
2400 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2402 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
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"));
2419 mute_button->set_text (_("m"));
2423 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2425 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2426 if (i != _main_automation_menu_map.end()) {
2430 i = _subplugin_menu_map.find (param);
2431 if (i != _subplugin_menu_map.end()) {
2439 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2441 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2443 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2447 gain_track.reset (new AutomationTimeAxisView (_session,
2448 _route, _route->amp(), c, param,
2453 _route->amp()->describe_parameter(param)));
2456 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2459 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2463 void add_region_to_list (RegionView* rv, RegionList* l)
2465 l->push_back (rv->region());
2469 RouteTimeAxisView::combine_regions ()
2471 /* as of may 2011, we do not offer uncombine for MIDI tracks
2474 if (!is_audio_track()) {
2482 RegionList selected_regions;
2483 boost::shared_ptr<Playlist> playlist = track()->playlist();
2485 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2487 if (selected_regions.size() < 2) {
2491 playlist->clear_changes ();
2492 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2494 _session->add_command (new StatefulDiffCommand (playlist));
2495 /* make the new region be selected */
2497 return _view->find_view (compound_region);
2501 RouteTimeAxisView::uncombine_regions ()
2503 /* as of may 2011, we do not offer uncombine for MIDI tracks
2505 if (!is_audio_track()) {
2513 RegionList selected_regions;
2514 boost::shared_ptr<Playlist> playlist = track()->playlist();
2516 /* have to grab selected regions first because the uncombine is going
2517 * to change that in the middle of the list traverse
2520 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2522 playlist->clear_changes ();
2524 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2525 playlist->uncombine (*i);
2528 _session->add_command (new StatefulDiffCommand (playlist));
2532 RouteTimeAxisView::state_id() const
2534 return string_compose ("rtav %1", _route->id().to_s());
2539 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2541 TimeAxisView::remove_child (c);
2543 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2545 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2546 if (i->second == a) {
2547 _automation_tracks.erase (i);