2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
91 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
93 , parent_canvas (canvas)
96 , route_group_button (_("g"))
97 , playlist_button (_("p"))
98 , automation_button (_("a"))
99 , automation_action_menu (0)
100 , plugins_submenu_item (0)
101 , route_group_menu (0)
102 , playlist_action_menu (0)
104 , color_mode_menu (0)
105 , gm (sess, true, 125, 18)
106 , _ignore_set_layer_display (false)
111 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
113 RouteUI::set_route (rt);
117 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
118 gm.get_level_meter().set_no_show_all();
119 gm.get_level_meter().setup_meters(50);
120 gm.update_gain_sensitive ();
122 string str = gui_property ("height");
124 set_height (atoi (str));
126 set_height (preset_height (HeightNormal));
129 if (!_route->is_hidden()) {
130 if (gui_property ("visible").empty()) {
131 set_gui_property ("visible", true);
134 set_gui_property ("visible", false);
138 update_solo_display ();
140 timestretch_rect = 0;
143 ignore_toggle = false;
145 route_group_button.set_name ("route button");
146 playlist_button.set_name ("route button");
147 automation_button.set_name ("route button");
149 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
150 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
151 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
157 switch (track()->mode()) {
159 case ARDOUR::NonLayered:
160 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
162 case ARDOUR::Destructive:
163 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
167 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
169 if (is_midi_track()) {
170 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
171 gm.set_fader_name ("MidiTrackFader");
173 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
174 gm.set_fader_name ("AudioTrackFader");
177 rec_enable_button->set_sensitive (_session->writable());
179 /* set playlist button tip to the current playlist, and make it update when it changes */
180 update_playlist_tip ();
181 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
184 gm.set_fader_name ("AudioBusFader");
187 controls_hbox.pack_start(gm.get_level_meter(), false, false);
188 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
189 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
190 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
192 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
194 if (!_route->is_master()) {
195 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
198 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
199 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
201 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
202 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
203 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
205 if (is_midi_track()) {
206 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
208 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
213 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
215 if (is_track() && track()->mode() == ARDOUR::Normal) {
216 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
221 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
222 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
226 str = gui_property ("layer-display");
228 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
231 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
232 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
234 /* pick up the correct freeze state */
239 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
240 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
241 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
243 PropertyList* plist = new PropertyList();
245 plist->add (ARDOUR::Properties::mute, true);
246 plist->add (ARDOUR::Properties::solo, true);
248 route_group_menu = new RouteGroupMenu (_session, plist);
250 // gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
252 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
255 RouteTimeAxisView::~RouteTimeAxisView ()
257 CatchDeletion (this);
259 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
263 delete playlist_action_menu;
264 playlist_action_menu = 0;
269 _automation_tracks.clear ();
271 delete route_group_menu;
275 RouteTimeAxisView::post_construct ()
277 /* map current state of the route */
279 update_diskstream_display ();
280 setup_processor_menu_and_curves ();
281 reset_processor_automation_curves ();
284 /** Set up the processor menu for the current set of processors, and
285 * display automation curves for any parameters which have data.
288 RouteTimeAxisView::setup_processor_menu_and_curves ()
290 _subplugin_menu_map.clear ();
291 subplugin_menu.items().clear ();
292 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
293 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
297 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
299 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
300 if (_route->route_group()) {
301 _route->route_group()->remove (_route);
307 r.push_back (route ());
309 route_group_menu->build (r);
310 route_group_menu->menu()->popup (ev->button, ev->time);
316 RouteTimeAxisView::playlist_changed ()
322 RouteTimeAxisView::label_view ()
324 string x = _route->name();
326 if (name_entry && x != name_entry->get_text()) {
327 name_entry->set_text (x);
328 ARDOUR_UI::instance()->set_tip (*name_entry, Glib::Markup::escape_text(x));
331 if (x != name_label.get_text()) {
332 name_label.set_text (x);
338 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
340 if (what_changed.contains (ARDOUR::Properties::name)) {
346 RouteTimeAxisView::take_name_changed (void *src)
354 RouteTimeAxisView::playlist_click ()
356 build_playlist_menu ();
357 conditionally_add_to_selection ();
358 playlist_action_menu->popup (1, gtk_get_current_event_time());
362 RouteTimeAxisView::automation_click ()
364 conditionally_add_to_selection ();
365 build_automation_action_menu (false);
366 automation_action_menu->popup (1, gtk_get_current_event_time());
370 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
372 using namespace Menu_Helpers;
374 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
375 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
378 detach_menu (subplugin_menu);
380 _main_automation_menu_map.clear ();
381 delete automation_action_menu;
382 automation_action_menu = new Menu;
384 MenuList& items = automation_action_menu->items();
386 automation_action_menu->set_name ("ArdourContextMenu");
388 items.push_back (MenuElem (_("Show All Automation"),
389 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
391 items.push_back (MenuElem (_("Show Existing Automation"),
392 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
394 items.push_back (MenuElem (_("Hide All Automation"),
395 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
397 items.push_back (SeparatorElem ());
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 (MenuElem (_("Plugins"), subplugin_menu));
405 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
410 RouteTimeAxisView::build_display_menu ()
412 using namespace Menu_Helpers;
416 TimeAxisView::build_display_menu ();
418 /* now fill it with our stuff */
420 MenuList& items = display_menu->items();
421 display_menu->set_name ("ArdourContextMenu");
423 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
426 detach_menu (*_size_menu);
429 items.push_back (MenuElem (_("Height"), *_size_menu));
431 items.push_back (SeparatorElem());
433 if (!Profile->get_sae()) {
434 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
435 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
436 items.push_back (SeparatorElem());
439 // Hook for derived classes to add type specific stuff
440 append_extra_display_menu_items ();
444 Menu* layers_menu = manage (new Menu);
445 MenuList &layers_items = layers_menu->items();
446 layers_menu->set_name("ArdourContextMenu");
448 RadioMenuItem::Group layers_group;
450 /* Find out how many overlaid/stacked tracks we have in the selection */
454 TrackSelection const & s = _editor.get_selection().tracks;
455 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
456 StreamView* v = (*i)->view ();
461 switch (v->layer_display ()) {
472 /* We're not connecting to signal_toggled() here; in the case where these two items are
473 set to be in the `inconsistent' state, it seems that one or other will end up active
474 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
475 select the active one, no toggled signal is emitted so nothing happens.
478 _ignore_set_layer_display = true;
480 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
481 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
482 i->set_active (overlaid != 0 && stacked == 0);
483 i->set_inconsistent (overlaid != 0 && stacked != 0);
484 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
486 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
487 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
488 i->set_active (overlaid == 0 && stacked != 0);
489 i->set_inconsistent (overlaid != 0 && stacked != 0);
490 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
492 _ignore_set_layer_display = false;
494 items.push_back (MenuElem (_("Layers"), *layers_menu));
496 if (!Profile->get_sae()) {
498 Menu* alignment_menu = manage (new Menu);
499 MenuList& alignment_items = alignment_menu->items();
500 alignment_menu->set_name ("ArdourContextMenu");
502 RadioMenuItem::Group align_group;
504 /* Same verbose hacks as for the layering options above */
510 boost::shared_ptr<Track> first_track;
512 TrackSelection const & s = _editor.get_selection().tracks;
513 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
514 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
515 if (!r || !r->is_track ()) {
520 first_track = r->track();
523 switch (r->track()->alignment_choice()) {
527 switch (r->track()->alignment_style()) {
528 case ExistingMaterial:
536 case UseExistingMaterial:
552 inconsistent = false;
561 if (!inconsistent && first_track) {
563 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
564 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
565 i->set_active (automatic != 0 && existing == 0 && capture == 0);
566 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
568 switch (first_track->alignment_choice()) {
570 switch (first_track->alignment_style()) {
571 case ExistingMaterial:
572 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
575 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
583 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
584 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
585 i->set_active (existing != 0 && capture == 0 && automatic == 0);
586 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
588 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
589 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
590 i->set_active (existing == 0 && capture != 0 && automatic == 0);
591 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
593 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
599 Menu* mode_menu = manage (new Menu);
600 MenuList& mode_items = mode_menu->items ();
601 mode_menu->set_name ("ArdourContextMenu");
603 RadioMenuItem::Group mode_group;
609 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
610 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
611 if (!r || !r->is_track ()) {
615 switch (r->track()->mode()) {
628 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
629 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
630 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
631 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
632 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
634 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
635 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
636 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
637 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
638 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
640 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
641 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
642 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
643 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
644 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
646 items.push_back (MenuElem (_("Mode"), *mode_menu));
649 color_mode_menu = build_color_mode_menu();
650 if (color_mode_menu) {
651 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
654 items.push_back (SeparatorElem());
656 build_playlist_menu ();
657 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
658 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
661 route_group_menu->detach ();
664 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
665 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
667 r.push_back (rtv->route ());
672 r.push_back (route ());
675 route_group_menu->build (r);
676 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
678 build_automation_action_menu (true);
679 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
681 items.push_back (SeparatorElem());
685 TrackSelection const & s = _editor.get_selection().tracks;
686 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
687 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
692 if (r->route()->active()) {
699 items.push_back (CheckMenuElem (_("Active")));
700 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
701 bool click_sets_active = true;
702 if (active > 0 && inactive == 0) {
703 i->set_active (true);
704 click_sets_active = false;
705 } else if (active > 0 && inactive > 0) {
706 i->set_inconsistent (true);
708 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
710 items.push_back (SeparatorElem());
711 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
712 if (!Profile->get_sae()) {
713 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
715 items.push_front (SeparatorElem());
716 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
721 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
723 if (apply_to_selection) {
724 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
729 if (!track()->can_use_mode (mode, needs_bounce)) {
735 cerr << "would bounce this one\n";
740 track()->set_mode (mode);
742 rec_enable_button->remove ();
745 case ARDOUR::NonLayered:
747 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
748 rec_enable_button->set_text (string());
750 case ARDOUR::Destructive:
751 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
752 rec_enable_button->set_text (string());
756 rec_enable_button->show_all ();
761 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
763 TimeAxisView::show_timestretch (start, end, layers, layer);
773 /* check that the time selection was made in our route, or our route group.
774 remember that route_group() == 0 implies the route is *not* in a edit group.
777 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
778 /* this doesn't apply to us */
782 /* ignore it if our edit group is not active */
784 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
789 if (timestretch_rect == 0) {
790 timestretch_rect = new SimpleRect (*canvas_display ());
791 timestretch_rect->property_x1() = 0.0;
792 timestretch_rect->property_y1() = 0.0;
793 timestretch_rect->property_x2() = 0.0;
794 timestretch_rect->property_y2() = 0.0;
795 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
796 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
799 timestretch_rect->show ();
800 timestretch_rect->raise_to_top ();
802 double const x1 = start / _editor.get_current_zoom();
803 double const x2 = (end - 1) / _editor.get_current_zoom();
805 timestretch_rect->property_x1() = x1;
806 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
807 timestretch_rect->property_x2() = x2;
808 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
812 RouteTimeAxisView::hide_timestretch ()
814 TimeAxisView::hide_timestretch ();
816 if (timestretch_rect) {
817 timestretch_rect->hide ();
822 RouteTimeAxisView::show_selection (TimeSelection& ts)
826 /* ignore it if our edit group is not active or if the selection was started
827 in some other track or route group (remember that route_group() == 0 means
828 that the track is not in an route group).
831 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
832 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
838 TimeAxisView::show_selection (ts);
842 RouteTimeAxisView::set_height (uint32_t h)
845 bool height_changed = (height == 0) || (h != height);
846 gm.get_level_meter().setup_meters (gmlen);
848 TimeAxisView::set_height (h);
851 _view->set_height ((double) current_height());
854 if (height >= preset_height (HeightNormal)) {
858 gm.get_gain_slider().show();
860 if (!_route || _route->is_monitor()) {
865 if (rec_enable_button)
866 rec_enable_button->show();
868 route_group_button.show();
869 automation_button.show();
871 if (is_track() && track()->mode() == ARDOUR::Normal) {
872 playlist_button.show();
879 gm.get_gain_slider().hide();
881 if (!_route || _route->is_monitor()) {
886 if (rec_enable_button)
887 rec_enable_button->show();
889 route_group_button.hide ();
890 automation_button.hide ();
892 if (is_track() && track()->mode() == ARDOUR::Normal) {
893 playlist_button.hide ();
898 if (height_changed && !no_redraw) {
899 /* only emit the signal if the height really changed */
905 RouteTimeAxisView::route_color_changed ()
908 _view->apply_color (color(), StreamView::RegionColor);
913 RouteTimeAxisView::reset_samples_per_unit ()
915 set_samples_per_unit (_editor.get_current_zoom());
919 RouteTimeAxisView::horizontal_position_changed ()
922 _view->horizontal_position_changed ();
927 RouteTimeAxisView::set_samples_per_unit (double spu)
932 speed = track()->speed();
936 _view->set_samples_per_unit (spu * speed);
939 TimeAxisView::set_samples_per_unit (spu * speed);
943 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
945 if (!mitem->get_active()) {
946 /* this is one of the two calls made when these radio menu items change status. this one
947 is for the item that became inactive, and we want to ignore it.
952 if (apply_to_selection) {
953 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
956 track()->set_align_choice (choice);
962 RouteTimeAxisView::rename_current_playlist ()
964 ArdourPrompter prompter (true);
967 boost::shared_ptr<Track> tr = track();
968 if (!tr || tr->destructive()) {
972 boost::shared_ptr<Playlist> pl = tr->playlist();
977 prompter.set_title (_("Rename Playlist"));
978 prompter.set_prompt (_("New name for playlist:"));
979 prompter.set_initial_text (pl->name());
980 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
981 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
983 switch (prompter.run ()) {
984 case Gtk::RESPONSE_ACCEPT:
985 prompter.get_result (name);
997 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
999 std::string ret (basename);
1001 std::string const group_string = "." + route_group()->name() + ".";
1003 // iterate through all playlists
1005 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1006 std::string tmp = (*i)->name();
1008 std::string::size_type idx = tmp.find(group_string);
1009 // find those which belong to this group
1010 if (idx != string::npos) {
1011 tmp = tmp.substr(idx + group_string.length());
1013 // and find the largest current number
1014 int x = atoi(tmp.c_str());
1015 if (x > maxnumber) {
1024 snprintf (buf, sizeof(buf), "%d", maxnumber);
1026 ret = this->name() + "." + route_group()->name () + "." + buf;
1032 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1036 boost::shared_ptr<Track> tr = track ();
1037 if (!tr || tr->destructive()) {
1041 boost::shared_ptr<const Playlist> pl = tr->playlist();
1048 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1049 name = resolve_new_group_playlist_name(name, playlists_before_op);
1052 while (_session->playlists->by_name(name)) {
1053 name = Playlist::bump_name (name, *_session);
1056 // TODO: The prompter "new" button should be de-activated if the user
1057 // specifies a playlist name which already exists in the session.
1061 ArdourPrompter prompter (true);
1063 prompter.set_title (_("New Copy Playlist"));
1064 prompter.set_prompt (_("Name for new playlist:"));
1065 prompter.set_initial_text (name);
1066 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1067 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1068 prompter.show_all ();
1070 switch (prompter.run ()) {
1071 case Gtk::RESPONSE_ACCEPT:
1072 prompter.get_result (name);
1080 if (name.length()) {
1081 tr->use_copy_playlist ();
1082 tr->playlist()->set_name (name);
1087 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1091 boost::shared_ptr<Track> tr = track ();
1092 if (!tr || tr->destructive()) {
1096 boost::shared_ptr<const Playlist> pl = tr->playlist();
1103 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1104 name = resolve_new_group_playlist_name(name,playlists_before_op);
1107 while (_session->playlists->by_name(name)) {
1108 name = Playlist::bump_name (name, *_session);
1114 ArdourPrompter prompter (true);
1116 prompter.set_title (_("New Playlist"));
1117 prompter.set_prompt (_("Name for new playlist:"));
1118 prompter.set_initial_text (name);
1119 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1120 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1122 switch (prompter.run ()) {
1123 case Gtk::RESPONSE_ACCEPT:
1124 prompter.get_result (name);
1132 if (name.length()) {
1133 tr->use_new_playlist ();
1134 tr->playlist()->set_name (name);
1139 RouteTimeAxisView::clear_playlist ()
1141 boost::shared_ptr<Track> tr = track ();
1142 if (!tr || tr->destructive()) {
1146 boost::shared_ptr<Playlist> pl = tr->playlist();
1151 _editor.clear_playlist (pl);
1155 RouteTimeAxisView::speed_changed ()
1157 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1161 RouteTimeAxisView::update_diskstream_display ()
1171 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1173 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1175 /* special case: select/deselect all tracks */
1176 if (_editor.get_selection().selected (this)) {
1177 _editor.get_selection().clear_tracks ();
1179 _editor.select_all_tracks ();
1185 switch (ArdourKeyboard::selection_type (ev->state)) {
1186 case Selection::Toggle:
1187 _editor.get_selection().toggle (this);
1190 case Selection::Set:
1191 _editor.get_selection().set (this);
1194 case Selection::Extend:
1195 _editor.extend_selection_to_track (*this);
1198 case Selection::Add:
1199 _editor.get_selection().add (this);
1205 RouteTimeAxisView::set_selected_points (PointSelection& points)
1207 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1208 (*i)->set_selected_points (points);
1213 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1216 _view->set_selected_regionviews (regions);
1220 /** Add the selectable things that we have to a list.
1221 * @param results List to add things to.
1224 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1229 speed = track()->speed();
1232 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1233 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1235 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1236 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1239 /* pick up visible automation tracks */
1241 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1242 if (!(*i)->hidden()) {
1243 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1249 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1252 _view->get_inverted_selectables (sel, results);
1255 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1256 if (!(*i)->hidden()) {
1257 (*i)->get_inverted_selectables (sel, results);
1265 RouteTimeAxisView::route_group () const
1267 return _route->route_group();
1271 RouteTimeAxisView::name() const
1273 return _route->name();
1276 boost::shared_ptr<Playlist>
1277 RouteTimeAxisView::playlist () const
1279 boost::shared_ptr<Track> tr;
1281 if ((tr = track()) != 0) {
1282 return tr->playlist();
1284 return boost::shared_ptr<Playlist> ();
1289 RouteTimeAxisView::name_entry_changed ()
1291 TimeAxisView::name_entry_changed ();
1293 string x = name_entry->get_text ();
1295 if (x == _route->name()) {
1299 strip_whitespace_edges (x);
1301 if (x.length() == 0) {
1302 name_entry->set_text (_route->name());
1306 if (_session->route_name_internal (x)) {
1307 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1309 name_entry->grab_focus ();
1310 } else if (RouteUI::verify_new_route_name (x)) {
1311 _route->set_name (x);
1313 name_entry->grab_focus ();
1317 boost::shared_ptr<Region>
1318 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1320 boost::shared_ptr<Playlist> pl = playlist ();
1323 return pl->find_next_region (pos, point, dir);
1326 return boost::shared_ptr<Region> ();
1330 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1332 boost::shared_ptr<Playlist> pl = playlist ();
1335 return pl->find_next_region_boundary (pos, dir);
1342 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1344 boost::shared_ptr<Playlist> what_we_got;
1345 boost::shared_ptr<Track> tr = track ();
1346 boost::shared_ptr<Playlist> playlist;
1349 /* route is a bus, not a track */
1353 playlist = tr->playlist();
1355 TimeSelection time (selection.time);
1356 float const speed = tr->speed();
1357 if (speed != 1.0f) {
1358 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1359 (*i).start = session_frame_to_track_frame((*i).start, speed);
1360 (*i).end = session_frame_to_track_frame((*i).end, speed);
1364 playlist->clear_changes ();
1365 playlist->clear_owned_changes ();
1369 if (playlist->cut (time) != 0) {
1370 vector<Command*> cmds;
1371 playlist->rdiff (cmds);
1372 _session->add_commands (cmds);
1374 _session->add_command (new StatefulDiffCommand (playlist));
1379 if ((what_we_got = playlist->cut (time)) != 0) {
1380 _editor.get_cut_buffer().add (what_we_got);
1381 vector<Command*> cmds;
1382 playlist->rdiff (cmds);
1383 _session->add_commands (cmds);
1385 _session->add_command (new StatefulDiffCommand (playlist));
1389 if ((what_we_got = playlist->copy (time)) != 0) {
1390 _editor.get_cut_buffer().add (what_we_got);
1395 if ((what_we_got = playlist->cut (time)) != 0) {
1397 vector<Command*> cmds;
1398 playlist->rdiff (cmds);
1399 _session->add_commands (cmds);
1400 _session->add_command (new StatefulDiffCommand (playlist));
1401 what_we_got->release ();
1408 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1414 boost::shared_ptr<Playlist> pl = playlist ();
1415 PlaylistSelection::iterator p;
1417 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1419 if (p == selection.playlists.end()) {
1423 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1425 if (track()->speed() != 1.0f) {
1426 pos = session_frame_to_track_frame (pos, track()->speed());
1427 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1430 pl->clear_changes ();
1431 pl->paste (*p, pos, times);
1432 _session->add_command (new StatefulDiffCommand (pl));
1438 struct PlaylistSorter {
1439 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1440 return a->sort_id() < b->sort_id();
1445 RouteTimeAxisView::build_playlist_menu ()
1447 using namespace Menu_Helpers;
1453 delete playlist_action_menu;
1454 playlist_action_menu = new Menu;
1455 playlist_action_menu->set_name ("ArdourContextMenu");
1457 MenuList& playlist_items = playlist_action_menu->items();
1458 playlist_action_menu->set_name ("ArdourContextMenu");
1459 playlist_items.clear();
1461 RadioMenuItem::Group playlist_group;
1462 boost::shared_ptr<Track> tr = track ();
1464 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1466 /* sort the playlists */
1468 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1470 /* add the playlists to the menu */
1471 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1472 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1473 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1474 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1476 if (tr->playlist()->id() == (*i)->id()) {
1482 playlist_items.push_back (SeparatorElem());
1483 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1484 playlist_items.push_back (SeparatorElem());
1486 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1487 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1488 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1491 // Use a label which tells the user what is happening
1492 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1493 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1497 playlist_items.push_back (SeparatorElem());
1498 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1499 playlist_items.push_back (SeparatorElem());
1501 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1505 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1507 assert (is_track());
1509 // exit if we were triggered by deactivating the old playlist
1510 if (!item->get_active()) {
1514 boost::shared_ptr<Playlist> pl (wpl.lock());
1520 if (track()->playlist() == pl) {
1521 // exit when use_playlist is called by the creation of the playlist menu
1522 // or the playlist choice is unchanged
1526 track()->use_playlist (pl);
1528 RouteGroup* rg = route_group();
1530 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1531 std::string group_string = "." + rg->name() + ".";
1533 std::string take_name = pl->name();
1534 std::string::size_type idx = take_name.find(group_string);
1536 if (idx == std::string::npos)
1539 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1541 boost::shared_ptr<RouteList> rl (rg->route_list());
1543 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1544 if ((*i) == this->route()) {
1548 std::string playlist_name = (*i)->name()+group_string+take_name;
1550 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1555 if (track->freeze_state() == Track::Frozen) {
1556 /* Don't change playlists of frozen tracks */
1560 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1562 // No playlist for this track for this take yet, make it
1563 track->use_new_playlist();
1564 track->playlist()->set_name(playlist_name);
1566 track->use_playlist(ipl);
1573 RouteTimeAxisView::update_playlist_tip ()
1575 RouteGroup* rg = route_group ();
1576 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1577 string group_string = "." + rg->name() + ".";
1579 string take_name = track()->playlist()->name();
1580 string::size_type idx = take_name.find(group_string);
1582 if (idx != string::npos) {
1583 /* find the bit containing the take number / name */
1584 take_name = take_name.substr (idx + group_string.length());
1586 /* set the playlist button tooltip to the take name */
1587 ARDOUR_UI::instance()->set_tip (
1589 string_compose(_("Take: %1.%2"),
1590 Glib::Markup::escape_text(rg->name()),
1591 Glib::Markup::escape_text(take_name))
1598 /* set the playlist button tooltip to the playlist name */
1599 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1604 RouteTimeAxisView::show_playlist_selector ()
1606 _editor.playlist_selector().show_for (this);
1610 RouteTimeAxisView::map_frozen ()
1616 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1618 switch (track()->freeze_state()) {
1620 playlist_button.set_sensitive (false);
1621 rec_enable_button->set_sensitive (false);
1624 playlist_button.set_sensitive (true);
1625 rec_enable_button->set_sensitive (true);
1631 RouteTimeAxisView::color_handler ()
1633 //case cTimeStretchOutline:
1634 if (timestretch_rect) {
1635 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1637 //case cTimeStretchFill:
1638 if (timestretch_rect) {
1639 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1645 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1646 * Will add track if necessary.
1649 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1651 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1652 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1655 /* it doesn't exist yet, so we don't care about the button state: just add it */
1656 create_automation_child (param, true);
1659 bool yn = menu->get_active();
1660 bool changed = false;
1662 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1664 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1665 will have done that for us.
1668 if (changed && !no_redraw) {
1676 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1678 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1684 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1686 if (menu && !_hidden) {
1687 ignore_toggle = true;
1688 menu->set_active (false);
1689 ignore_toggle = false;
1692 if (_route && !no_redraw) {
1699 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1701 if (apply_to_selection) {
1702 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1706 /* Show our automation */
1708 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1709 i->second->set_marked_for_display (true);
1711 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1714 menu->set_active(true);
1719 /* Show processor automation */
1721 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1722 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1723 if ((*ii)->view == 0) {
1724 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1727 (*ii)->menu_item->set_active (true);
1740 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1742 if (apply_to_selection) {
1743 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1747 /* Show our automation */
1749 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1750 if (i->second->has_automation()) {
1751 i->second->set_marked_for_display (true);
1753 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1755 menu->set_active(true);
1760 /* Show processor automation */
1762 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1763 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1764 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1765 (*ii)->menu_item->set_active (true);
1777 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1779 if (apply_to_selection) {
1780 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1784 /* Hide our automation */
1786 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1787 i->second->set_marked_for_display (false);
1789 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1792 menu->set_active (false);
1796 /* Hide processor automation */
1798 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1799 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1800 (*ii)->menu_item->set_active (false);
1811 RouteTimeAxisView::region_view_added (RegionView* rv)
1813 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1814 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1815 boost::shared_ptr<AutomationTimeAxisView> atv;
1817 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1822 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1823 (*i)->add_ghost(rv);
1827 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1829 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1835 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1837 parent.remove_processor_automation_node (this);
1841 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1844 remove_child (pan->view);
1848 RouteTimeAxisView::ProcessorAutomationNode*
1849 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1851 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1853 if ((*i)->processor == processor) {
1855 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1856 if ((*ii)->what == what) {
1866 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1868 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1871 ProcessorAutomationNode* pan;
1873 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1874 /* session state may never have been saved with new plugin */
1875 error << _("programming error: ")
1876 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1877 processor->name(), what.type(), (int) what.channel(), what.id() )
1887 boost::shared_ptr<AutomationControl> control
1888 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1890 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1891 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1892 _editor, *this, false, parent_canvas,
1893 processor->describe_parameter (what), processor->name()));
1895 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1897 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1900 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1905 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1908 pan->menu_item->set_active (false);
1917 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1919 boost::shared_ptr<Processor> processor (p.lock ());
1921 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1922 /* The Amp processor is a special case and is dealt with separately */
1926 set<Evoral::Parameter> existing;
1928 processor->what_has_data (existing);
1930 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1932 Evoral::Parameter param (*i);
1933 boost::shared_ptr<AutomationLine> al;
1935 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1938 add_processor_automation_curve (processor, param);
1944 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1946 using namespace Menu_Helpers;
1950 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1952 _automation_tracks[param] = track;
1954 /* existing state overrides "show" argument */
1955 string s = track->gui_property ("visible");
1957 show = string_is_affirmative (s);
1960 /* this might or might not change the visibility status, so don't rely on it */
1961 track->set_marked_for_display (show);
1963 if (show && !no_redraw) {
1967 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1968 /* MIDI-related parameters are always in the menu, there's no
1969 reason to rebuild the menu just because we added a automation
1970 lane for one of them. But if we add a non-MIDI automation
1971 lane, then we need to invalidate the display menu.
1973 delete display_menu;
1979 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1981 boost::shared_ptr<Processor> processor (p.lock ());
1983 if (!processor || !processor->display_to_user ()) {
1987 /* we use this override to veto the Amp processor from the plugin menu,
1988 as its automation lane can be accessed using the special "Fader" menu
1992 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1996 using namespace Menu_Helpers;
1997 ProcessorAutomationInfo *rai;
1998 list<ProcessorAutomationInfo*>::iterator x;
2000 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2002 if (automatable.empty()) {
2006 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2007 if ((*x)->processor == processor) {
2012 if (x == processor_automation.end()) {
2014 rai = new ProcessorAutomationInfo (processor);
2015 processor_automation.push_back (rai);
2023 /* any older menu was deleted at the top of processors_changed()
2024 when we cleared the subplugin menu.
2027 rai->menu = manage (new Menu);
2028 MenuList& items = rai->menu->items();
2029 rai->menu->set_name ("ArdourContextMenu");
2033 std::set<Evoral::Parameter> has_visible_automation;
2034 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2036 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2038 ProcessorAutomationNode* pan;
2039 CheckMenuItem* mitem;
2041 string name = processor->describe_parameter (*i);
2043 items.push_back (CheckMenuElem (name));
2044 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2046 _subplugin_menu_map[*i] = mitem;
2048 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2049 mitem->set_active(true);
2052 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2056 pan = new ProcessorAutomationNode (*i, mitem, *this);
2058 rai->lines.push_back (pan);
2062 pan->menu_item = mitem;
2066 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2069 /* add the menu for this processor, because the subplugin
2070 menu is always cleared at the top of processors_changed().
2071 this is the result of some poor design in gtkmm and/or
2075 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2080 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2081 RouteTimeAxisView::ProcessorAutomationNode* pan)
2083 bool showit = pan->menu_item->get_active();
2084 bool redraw = false;
2086 if (pan->view == 0 && showit) {
2087 add_processor_automation_curve (rai->processor, pan->what);
2091 if (pan->view && pan->view->set_marked_for_display (showit)) {
2095 if (redraw && !no_redraw) {
2101 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2103 if (c.type == RouteProcessorChange::MeterPointChange) {
2104 /* nothing to do if only the meter point has changed */
2108 using namespace Menu_Helpers;
2110 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2111 (*i)->valid = false;
2114 setup_processor_menu_and_curves ();
2116 bool deleted_processor_automation = false;
2118 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2120 list<ProcessorAutomationInfo*>::iterator tmp;
2128 processor_automation.erase (i);
2129 deleted_processor_automation = true;
2136 if (deleted_processor_automation && !no_redraw) {
2141 boost::shared_ptr<AutomationLine>
2142 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2144 ProcessorAutomationNode* pan;
2146 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2152 return boost::shared_ptr<AutomationLine>();
2156 RouteTimeAxisView::reset_processor_automation_curves ()
2158 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2164 RouteTimeAxisView::can_edit_name () const
2166 /* we do not allow track name changes if it is record enabled
2168 return !_route->record_enabled();
2172 RouteTimeAxisView::update_rec_display ()
2174 RouteUI::update_rec_display ();
2178 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2180 if (_ignore_set_layer_display) {
2184 if (apply_to_selection) {
2185 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2189 _view->set_layer_display (d);
2192 set_gui_property (X_("layer-display"), enum_2_string (d));
2197 RouteTimeAxisView::layer_display () const
2200 return _view->layer_display ();
2203 /* we don't know, since we don't have a _view, so just return something */
2209 boost::shared_ptr<AutomationTimeAxisView>
2210 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2212 AutomationTracks::iterator i = _automation_tracks.find(param);
2213 if (i != _automation_tracks.end()) {
2216 return boost::shared_ptr<AutomationTimeAxisView>();
2221 RouteTimeAxisView::fast_update ()
2223 gm.get_level_meter().update_meters ();
2227 RouteTimeAxisView::hide_meter ()
2230 gm.get_level_meter().hide_meters ();
2234 RouteTimeAxisView::show_meter ()
2240 RouteTimeAxisView::reset_meter ()
2242 if (Config->get_show_track_meters()) {
2243 gm.get_level_meter().setup_meters (height-5);
2250 RouteTimeAxisView::clear_meter ()
2252 gm.get_level_meter().clear_meters ();
2256 RouteTimeAxisView::meter_changed ()
2258 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2263 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2269 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2271 using namespace Menu_Helpers;
2273 if (!_underlay_streams.empty()) {
2274 MenuList& parent_items = parent_menu->items();
2275 Menu* gs_menu = manage (new Menu);
2276 gs_menu->set_name ("ArdourContextMenu");
2277 MenuList& gs_items = gs_menu->items();
2279 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2281 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2282 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2283 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2289 RouteTimeAxisView::set_underlay_state()
2291 if (!underlay_xml_node) {
2295 XMLNodeList nlist = underlay_xml_node->children();
2296 XMLNodeConstIterator niter;
2297 XMLNode *child_node;
2299 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2300 child_node = *niter;
2302 if (child_node->name() != "Underlay") {
2306 XMLProperty* prop = child_node->property ("id");
2308 PBD::ID id (prop->value());
2310 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2313 add_underlay(v->view(), false);
2322 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2328 RouteTimeAxisView& other = v->trackview();
2330 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2331 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2332 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2336 _underlay_streams.push_back(v);
2337 other._underlay_mirrors.push_back(this);
2339 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2341 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2343 if (!underlay_xml_node) {
2344 underlay_xml_node = xml_node->add_child("Underlays");
2347 XMLNode* node = underlay_xml_node->add_child("Underlay");
2348 XMLProperty* prop = node->add_property("id");
2349 prop->set_value(v->trackview().route()->id().to_s());
2356 RouteTimeAxisView::remove_underlay (StreamView* v)
2362 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2363 RouteTimeAxisView& other = v->trackview();
2365 if (it != _underlay_streams.end()) {
2366 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2368 if (gm == other._underlay_mirrors.end()) {
2369 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2373 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2375 _underlay_streams.erase(it);
2376 other._underlay_mirrors.erase(gm);
2378 if (underlay_xml_node) {
2379 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2385 RouteTimeAxisView::set_button_names ()
2387 if (_route && _route->solo_safe()) {
2388 solo_button->remove ();
2389 if (solo_safe_pixbuf == 0) {
2390 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2392 solo_button->set_image (solo_safe_pixbuf);
2393 solo_button->set_text (string());
2395 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2396 if (Config->get_solo_control_is_listen_control()) {
2397 switch (Config->get_listen_position()) {
2398 case AfterFaderListen:
2399 solo_button->set_text (_("A"));
2400 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2402 case PreFaderListen:
2403 solo_button->set_text (_("P"));
2404 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2408 solo_button->set_text (_("s"));
2409 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2412 mute_button->set_text (_("m"));
2416 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2418 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2419 if (i != _main_automation_menu_map.end()) {
2423 i = _subplugin_menu_map.find (param);
2424 if (i != _subplugin_menu_map.end()) {
2432 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2434 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2436 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2440 gain_track.reset (new AutomationTimeAxisView (_session,
2441 _route, _route->amp(), c, param,
2446 _route->amp()->describe_parameter(param)));
2449 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2452 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2456 void add_region_to_list (RegionView* rv, RegionList* l)
2458 l->push_back (rv->region());
2462 RouteTimeAxisView::combine_regions ()
2464 /* as of may 2011, we do not offer uncombine for MIDI tracks
2467 if (!is_audio_track()) {
2475 RegionList selected_regions;
2476 boost::shared_ptr<Playlist> playlist = track()->playlist();
2478 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2480 if (selected_regions.size() < 2) {
2484 playlist->clear_changes ();
2485 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2487 _session->add_command (new StatefulDiffCommand (playlist));
2488 /* make the new region be selected */
2490 return _view->find_view (compound_region);
2494 RouteTimeAxisView::uncombine_regions ()
2496 /* as of may 2011, we do not offer uncombine for MIDI tracks
2498 if (!is_audio_track()) {
2506 RegionList selected_regions;
2507 boost::shared_ptr<Playlist> playlist = track()->playlist();
2509 /* have to grab selected regions first because the uncombine is going
2510 * to change that in the middle of the list traverse
2513 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2515 playlist->clear_changes ();
2517 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2518 playlist->uncombine (*i);
2521 _session->add_command (new StatefulDiffCommand (playlist));
2525 RouteTimeAxisView::state_id() const
2527 return string_compose ("rtav %1", _route->id().to_s());
2532 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2534 TimeAxisView::remove_child (c);
2536 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2538 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2539 if (i->second == a) {
2540 _automation_tracks.erase (i);