2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/event_type_map.h"
47 #include "ardour/processor.h"
48 #include "ardour/profile.h"
49 #include "ardour/route_group.h"
50 #include "ardour/session.h"
51 #include "ardour/session_playlists.h"
52 #include "evoral/Parameter.hpp"
54 #include "ardour_ui.h"
55 #include "ardour_button.h"
57 #include "global_signals.h"
58 #include "route_time_axis.h"
59 #include "automation_time_axis.h"
60 #include "canvas_impl.h"
62 #include "gui_thread.h"
64 #include "playlist_selector.h"
65 #include "point_selection.h"
67 #include "public_editor.h"
68 #include "region_view.h"
69 #include "rgb_macros.h"
70 #include "selection.h"
71 #include "simplerect.h"
72 #include "streamview.h"
74 #include "route_group_menu.h"
76 #include "ardour/track.h"
80 using namespace ARDOUR;
82 using namespace Gtkmm2ext;
84 using namespace Editing;
88 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
91 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
93 , parent_canvas (canvas)
96 , route_group_button (_("g"))
97 , playlist_button (_("p"))
98 , automation_button (_("a"))
99 , automation_action_menu (0)
100 , plugins_submenu_item (0)
101 , route_group_menu (0)
102 , playlist_action_menu (0)
104 , color_mode_menu (0)
105 , gm (sess, true, 125, 18)
106 , _ignore_set_layer_display (false)
111 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
113 RouteUI::set_route (rt);
115 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
116 gm.get_level_meter().set_no_show_all();
117 gm.get_level_meter().setup_meters(50);
118 gm.update_gain_sensitive ();
120 string str = gui_property ("height");
122 set_height (atoi (str));
124 set_height (preset_height (HeightNormal));
127 if (!_route->is_hidden()) {
128 if (gui_property ("visible").empty()) {
129 set_gui_property ("visible", true);
132 set_gui_property ("visible", false);
136 update_solo_display ();
138 timestretch_rect = 0;
141 ignore_toggle = false;
143 route_group_button.set_name ("route button");
144 playlist_button.set_name ("route button");
145 automation_button.set_name ("route button");
147 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
148 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
149 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
155 switch (track()->mode()) {
157 case ARDOUR::NonLayered:
158 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
160 case ARDOUR::Destructive:
161 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
165 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
167 if (is_midi_track()) {
168 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
169 gm.set_fader_name ("MidiTrackFader");
171 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
172 gm.set_fader_name ("AudioTrackFader");
175 rec_enable_button->set_sensitive (_session->writable());
177 /* set playlist button tip to the current playlist, and make it update when it changes */
178 update_playlist_tip ();
179 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
182 gm.set_fader_name ("AudioBusFader");
185 controls_hbox.pack_start(gm.get_level_meter(), false, false);
186 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
187 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
188 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
190 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 if (!_route->is_master()) {
193 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
196 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
197 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
199 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
200 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
201 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
203 if (is_midi_track()) {
204 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
206 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
211 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
213 if (is_track() && track()->mode() == ARDOUR::Normal) {
214 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
219 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
220 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
224 str = gui_property ("layer-display");
226 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
229 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
230 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
232 /* pick up the correct freeze state */
237 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
238 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
239 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
241 PropertyList* plist = new PropertyList();
243 plist->add (ARDOUR::Properties::mute, true);
244 plist->add (ARDOUR::Properties::solo, true);
246 route_group_menu = new RouteGroupMenu (_session, plist);
248 // gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
250 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
253 RouteTimeAxisView::~RouteTimeAxisView ()
255 CatchDeletion (this);
257 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
261 delete playlist_action_menu;
262 playlist_action_menu = 0;
267 _automation_tracks.clear ();
269 delete route_group_menu;
273 RouteTimeAxisView::post_construct ()
275 /* map current state of the route */
277 update_diskstream_display ();
278 setup_processor_menu_and_curves ();
279 reset_processor_automation_curves ();
282 /** Set up the processor menu for the current set of processors, and
283 * display automation curves for any parameters which have data.
286 RouteTimeAxisView::setup_processor_menu_and_curves ()
288 _subplugin_menu_map.clear ();
289 subplugin_menu.items().clear ();
290 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
291 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
295 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
297 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
298 if (_route->route_group()) {
299 _route->route_group()->remove (_route);
305 r.push_back (route ());
307 route_group_menu->build (r);
308 route_group_menu->menu()->popup (ev->button, ev->time);
314 RouteTimeAxisView::playlist_changed ()
320 RouteTimeAxisView::label_view ()
322 string x = _route->name();
324 if (x != name_entry.get_text()) {
325 name_entry.set_text (x);
328 if (x != name_label.get_text()) {
329 name_label.set_text (x);
332 ARDOUR_UI::instance()->set_tip (name_entry, Glib::Markup::escape_text(x));
336 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
338 if (what_changed.contains (ARDOUR::Properties::name)) {
344 RouteTimeAxisView::take_name_changed (void *src)
352 RouteTimeAxisView::playlist_click ()
354 build_playlist_menu ();
355 conditionally_add_to_selection ();
356 playlist_action_menu->popup (1, gtk_get_current_event_time());
360 RouteTimeAxisView::automation_click ()
362 conditionally_add_to_selection ();
363 build_automation_action_menu (false);
364 automation_action_menu->popup (1, gtk_get_current_event_time());
368 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
370 using namespace Menu_Helpers;
372 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
373 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
376 detach_menu (subplugin_menu);
378 _main_automation_menu_map.clear ();
379 delete automation_action_menu;
380 automation_action_menu = new Menu;
382 MenuList& items = automation_action_menu->items();
384 automation_action_menu->set_name ("ArdourContextMenu");
386 items.push_back (MenuElem (_("Show All Automation"),
387 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
389 items.push_back (MenuElem (_("Show Existing Automation"),
390 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
392 items.push_back (MenuElem (_("Hide All Automation"),
393 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
395 items.push_back (SeparatorElem ());
397 /* Attach the plugin submenu. It may have previously been used elsewhere,
398 so it was detached above
401 if (!subplugin_menu.items().empty()) {
402 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
403 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
408 RouteTimeAxisView::build_display_menu ()
410 using namespace Menu_Helpers;
414 TimeAxisView::build_display_menu ();
416 /* now fill it with our stuff */
418 MenuList& items = display_menu->items();
419 display_menu->set_name ("ArdourContextMenu");
421 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
424 detach_menu (*_size_menu);
427 items.push_back (MenuElem (_("Height"), *_size_menu));
429 items.push_back (SeparatorElem());
431 if (!Profile->get_sae()) {
432 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
433 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
434 items.push_back (SeparatorElem());
437 // Hook for derived classes to add type specific stuff
438 append_extra_display_menu_items ();
442 Menu* layers_menu = manage (new Menu);
443 MenuList &layers_items = layers_menu->items();
444 layers_menu->set_name("ArdourContextMenu");
446 RadioMenuItem::Group layers_group;
448 /* Find out how many overlaid/stacked tracks we have in the selection */
452 TrackSelection const & s = _editor.get_selection().tracks;
453 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
454 StreamView* v = (*i)->view ();
459 switch (v->layer_display ()) {
470 /* We're not connecting to signal_toggled() here; in the case where these two items are
471 set to be in the `inconsistent' state, it seems that one or other will end up active
472 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
473 select the active one, no toggled signal is emitted so nothing happens.
476 _ignore_set_layer_display = true;
478 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
479 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
480 i->set_active (overlaid != 0 && stacked == 0);
481 i->set_inconsistent (overlaid != 0 && stacked != 0);
482 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
484 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
485 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
486 i->set_active (overlaid == 0 && stacked != 0);
487 i->set_inconsistent (overlaid != 0 && stacked != 0);
488 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
490 _ignore_set_layer_display = false;
492 items.push_back (MenuElem (_("Layers"), *layers_menu));
494 if (!Profile->get_sae()) {
496 Menu* alignment_menu = manage (new Menu);
497 MenuList& alignment_items = alignment_menu->items();
498 alignment_menu->set_name ("ArdourContextMenu");
500 RadioMenuItem::Group align_group;
502 /* Same verbose hacks as for the layering options above */
508 boost::shared_ptr<Track> first_track;
510 TrackSelection const & s = _editor.get_selection().tracks;
511 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
512 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
513 if (!r || !r->is_track ()) {
518 first_track = r->track();
521 switch (r->track()->alignment_choice()) {
525 switch (r->track()->alignment_style()) {
526 case ExistingMaterial:
534 case UseExistingMaterial:
550 inconsistent = false;
559 if (!inconsistent && first_track) {
561 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
562 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
563 i->set_active (automatic != 0 && existing == 0 && capture == 0);
564 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
566 switch (first_track->alignment_choice()) {
568 switch (first_track->alignment_style()) {
569 case ExistingMaterial:
570 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
573 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
581 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
582 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
583 i->set_active (existing != 0 && capture == 0 && automatic == 0);
584 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
586 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
587 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
588 i->set_active (existing == 0 && capture != 0 && automatic == 0);
589 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
591 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
597 Menu* mode_menu = manage (new Menu);
598 MenuList& mode_items = mode_menu->items ();
599 mode_menu->set_name ("ArdourContextMenu");
601 RadioMenuItem::Group mode_group;
607 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
608 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
609 if (!r || !r->is_track ()) {
613 switch (r->track()->mode()) {
626 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
627 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
628 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
629 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
630 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
632 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
633 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
634 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
635 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
636 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
638 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
641 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
642 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
644 items.push_back (MenuElem (_("Mode"), *mode_menu));
647 color_mode_menu = build_color_mode_menu();
648 if (color_mode_menu) {
649 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
652 items.push_back (SeparatorElem());
654 build_playlist_menu ();
655 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
656 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
659 route_group_menu->detach ();
662 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
663 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
665 r.push_back (rtv->route ());
670 r.push_back (route ());
673 route_group_menu->build (r);
674 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
676 build_automation_action_menu (true);
677 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
679 items.push_back (SeparatorElem());
683 TrackSelection const & s = _editor.get_selection().tracks;
684 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
685 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
690 if (r->route()->active()) {
697 items.push_back (CheckMenuElem (_("Active")));
698 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
699 bool click_sets_active = true;
700 if (active > 0 && inactive == 0) {
701 i->set_active (true);
702 click_sets_active = false;
703 } else if (active > 0 && inactive > 0) {
704 i->set_inconsistent (true);
706 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
708 items.push_back (SeparatorElem());
709 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
710 if (!Profile->get_sae()) {
711 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
713 items.push_front (SeparatorElem());
714 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
719 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
721 if (apply_to_selection) {
722 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
727 if (!track()->can_use_mode (mode, needs_bounce)) {
733 cerr << "would bounce this one\n";
738 track()->set_mode (mode);
740 rec_enable_button->remove ();
743 case ARDOUR::NonLayered:
745 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
746 rec_enable_button->set_text (string());
748 case ARDOUR::Destructive:
749 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
750 rec_enable_button->set_text (string());
754 rec_enable_button->show_all ();
759 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
761 TimeAxisView::show_timestretch (start, end, layers, layer);
771 /* check that the time selection was made in our route, or our route group.
772 remember that route_group() == 0 implies the route is *not* in a edit group.
775 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
776 /* this doesn't apply to us */
780 /* ignore it if our edit group is not active */
782 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
787 if (timestretch_rect == 0) {
788 timestretch_rect = new SimpleRect (*canvas_display ());
789 timestretch_rect->property_x1() = 0.0;
790 timestretch_rect->property_y1() = 0.0;
791 timestretch_rect->property_x2() = 0.0;
792 timestretch_rect->property_y2() = 0.0;
793 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
794 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
797 timestretch_rect->show ();
798 timestretch_rect->raise_to_top ();
800 double const x1 = start / _editor.get_current_zoom();
801 double const x2 = (end - 1) / _editor.get_current_zoom();
803 timestretch_rect->property_x1() = x1;
804 timestretch_rect->property_y1() = current_height() * (layers - layer - 1) / layers;
805 timestretch_rect->property_x2() = x2;
806 timestretch_rect->property_y2() = current_height() * (layers - layer) / layers;
810 RouteTimeAxisView::hide_timestretch ()
812 TimeAxisView::hide_timestretch ();
814 if (timestretch_rect) {
815 timestretch_rect->hide ();
820 RouteTimeAxisView::show_selection (TimeSelection& ts)
824 /* ignore it if our edit group is not active or if the selection was started
825 in some other track or route group (remember that route_group() == 0 means
826 that the track is not in an route group).
829 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
830 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
836 TimeAxisView::show_selection (ts);
840 RouteTimeAxisView::set_height (uint32_t h)
843 bool height_changed = (height == 0) || (h != height);
844 gm.get_level_meter().setup_meters (gmlen);
846 TimeAxisView::set_height (h);
849 _view->set_height ((double) current_height());
852 if (height >= preset_height (HeightNormal)) {
856 gm.get_gain_slider().show();
858 if (!_route || _route->is_monitor()) {
863 if (rec_enable_button)
864 rec_enable_button->show();
866 route_group_button.show();
867 automation_button.show();
869 if (is_track() && track()->mode() == ARDOUR::Normal) {
870 playlist_button.show();
877 gm.get_gain_slider().hide();
879 if (!_route || _route->is_monitor()) {
884 if (rec_enable_button)
885 rec_enable_button->show();
887 route_group_button.hide ();
888 automation_button.hide ();
890 if (is_track() && track()->mode() == ARDOUR::Normal) {
891 playlist_button.hide ();
896 if (height_changed && !no_redraw) {
897 /* only emit the signal if the height really changed */
903 RouteTimeAxisView::route_color_changed ()
906 _view->apply_color (color(), StreamView::RegionColor);
911 RouteTimeAxisView::reset_samples_per_unit ()
913 set_samples_per_unit (_editor.get_current_zoom());
917 RouteTimeAxisView::horizontal_position_changed ()
920 _view->horizontal_position_changed ();
925 RouteTimeAxisView::set_samples_per_unit (double spu)
930 speed = track()->speed();
934 _view->set_samples_per_unit (spu * speed);
937 TimeAxisView::set_samples_per_unit (spu * speed);
941 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
943 if (!mitem->get_active()) {
944 /* this is one of the two calls made when these radio menu items change status. this one
945 is for the item that became inactive, and we want to ignore it.
950 if (apply_to_selection) {
951 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
954 track()->set_align_choice (choice);
960 RouteTimeAxisView::rename_current_playlist ()
962 ArdourPrompter prompter (true);
965 boost::shared_ptr<Track> tr = track();
966 if (!tr || tr->destructive()) {
970 boost::shared_ptr<Playlist> pl = tr->playlist();
975 prompter.set_title (_("Rename Playlist"));
976 prompter.set_prompt (_("New name for playlist:"));
977 prompter.set_initial_text (pl->name());
978 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
979 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
981 switch (prompter.run ()) {
982 case Gtk::RESPONSE_ACCEPT:
983 prompter.get_result (name);
995 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
997 std::string ret (basename);
999 std::string const group_string = "." + route_group()->name() + ".";
1001 // iterate through all playlists
1003 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1004 std::string tmp = (*i)->name();
1006 std::string::size_type idx = tmp.find(group_string);
1007 // find those which belong to this group
1008 if (idx != string::npos) {
1009 tmp = tmp.substr(idx + group_string.length());
1011 // and find the largest current number
1012 int x = atoi(tmp.c_str());
1013 if (x > maxnumber) {
1022 snprintf (buf, sizeof(buf), "%d", maxnumber);
1024 ret = this->name() + "." + route_group()->name () + "." + buf;
1030 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1034 boost::shared_ptr<Track> tr = track ();
1035 if (!tr || tr->destructive()) {
1039 boost::shared_ptr<const Playlist> pl = tr->playlist();
1046 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1047 name = resolve_new_group_playlist_name(name, playlists_before_op);
1050 while (_session->playlists->by_name(name)) {
1051 name = Playlist::bump_name (name, *_session);
1054 // TODO: The prompter "new" button should be de-activated if the user
1055 // specifies a playlist name which already exists in the session.
1059 ArdourPrompter prompter (true);
1061 prompter.set_title (_("New Copy Playlist"));
1062 prompter.set_prompt (_("Name for new playlist:"));
1063 prompter.set_initial_text (name);
1064 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1065 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1066 prompter.show_all ();
1068 switch (prompter.run ()) {
1069 case Gtk::RESPONSE_ACCEPT:
1070 prompter.get_result (name);
1078 if (name.length()) {
1079 tr->use_copy_playlist ();
1080 tr->playlist()->set_name (name);
1085 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1089 boost::shared_ptr<Track> tr = track ();
1090 if (!tr || tr->destructive()) {
1094 boost::shared_ptr<const Playlist> pl = tr->playlist();
1101 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1102 name = resolve_new_group_playlist_name(name,playlists_before_op);
1105 while (_session->playlists->by_name(name)) {
1106 name = Playlist::bump_name (name, *_session);
1112 ArdourPrompter prompter (true);
1114 prompter.set_title (_("New Playlist"));
1115 prompter.set_prompt (_("Name for new playlist:"));
1116 prompter.set_initial_text (name);
1117 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1118 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1120 switch (prompter.run ()) {
1121 case Gtk::RESPONSE_ACCEPT:
1122 prompter.get_result (name);
1130 if (name.length()) {
1131 tr->use_new_playlist ();
1132 tr->playlist()->set_name (name);
1137 RouteTimeAxisView::clear_playlist ()
1139 boost::shared_ptr<Track> tr = track ();
1140 if (!tr || tr->destructive()) {
1144 boost::shared_ptr<Playlist> pl = tr->playlist();
1149 _editor.clear_playlist (pl);
1153 RouteTimeAxisView::speed_changed ()
1155 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1159 RouteTimeAxisView::update_diskstream_display ()
1169 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1171 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1173 /* special case: select/deselect all tracks */
1174 if (_editor.get_selection().selected (this)) {
1175 _editor.get_selection().clear_tracks ();
1177 _editor.select_all_tracks ();
1183 switch (ArdourKeyboard::selection_type (ev->state)) {
1184 case Selection::Toggle:
1185 _editor.get_selection().toggle (this);
1188 case Selection::Set:
1189 _editor.get_selection().set (this);
1192 case Selection::Extend:
1193 _editor.extend_selection_to_track (*this);
1196 case Selection::Add:
1197 _editor.get_selection().add (this);
1203 RouteTimeAxisView::set_selected_points (PointSelection& points)
1205 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1206 (*i)->set_selected_points (points);
1211 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1214 _view->set_selected_regionviews (regions);
1218 /** Add the selectable things that we have to a list.
1219 * @param results List to add things to.
1222 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1227 speed = track()->speed();
1230 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1231 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1233 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1234 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1237 /* pick up visible automation tracks */
1239 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1240 if (!(*i)->hidden()) {
1241 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1247 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1250 _view->get_inverted_selectables (sel, results);
1253 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1254 if (!(*i)->hidden()) {
1255 (*i)->get_inverted_selectables (sel, results);
1263 RouteTimeAxisView::route_group () const
1265 return _route->route_group();
1269 RouteTimeAxisView::name() const
1271 return _route->name();
1274 boost::shared_ptr<Playlist>
1275 RouteTimeAxisView::playlist () const
1277 boost::shared_ptr<Track> tr;
1279 if ((tr = track()) != 0) {
1280 return tr->playlist();
1282 return boost::shared_ptr<Playlist> ();
1287 RouteTimeAxisView::name_entry_changed ()
1289 string x = name_entry.get_text ();
1291 if (x == _route->name()) {
1295 strip_whitespace_edges (x);
1297 if (x.length() == 0) {
1298 name_entry.set_text (_route->name());
1302 if (_session->route_name_internal (x)) {
1303 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1305 name_entry.grab_focus ();
1306 } else if (RouteUI::verify_new_route_name (x)) {
1307 _route->set_name (x);
1309 name_entry.grab_focus ();
1313 boost::shared_ptr<Region>
1314 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1316 boost::shared_ptr<Playlist> pl = playlist ();
1319 return pl->find_next_region (pos, point, dir);
1322 return boost::shared_ptr<Region> ();
1326 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1328 boost::shared_ptr<Playlist> pl = playlist ();
1331 return pl->find_next_region_boundary (pos, dir);
1338 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1340 boost::shared_ptr<Playlist> what_we_got;
1341 boost::shared_ptr<Track> tr = track ();
1342 boost::shared_ptr<Playlist> playlist;
1345 /* route is a bus, not a track */
1349 playlist = tr->playlist();
1351 TimeSelection time (selection.time);
1352 float const speed = tr->speed();
1353 if (speed != 1.0f) {
1354 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1355 (*i).start = session_frame_to_track_frame((*i).start, speed);
1356 (*i).end = session_frame_to_track_frame((*i).end, speed);
1360 playlist->clear_changes ();
1361 playlist->clear_owned_changes ();
1365 if (playlist->cut (time) != 0) {
1366 vector<Command*> cmds;
1367 playlist->rdiff (cmds);
1368 _session->add_commands (cmds);
1370 _session->add_command (new StatefulDiffCommand (playlist));
1375 if ((what_we_got = playlist->cut (time)) != 0) {
1376 _editor.get_cut_buffer().add (what_we_got);
1377 vector<Command*> cmds;
1378 playlist->rdiff (cmds);
1379 _session->add_commands (cmds);
1381 _session->add_command (new StatefulDiffCommand (playlist));
1385 if ((what_we_got = playlist->copy (time)) != 0) {
1386 _editor.get_cut_buffer().add (what_we_got);
1391 if ((what_we_got = playlist->cut (time)) != 0) {
1393 vector<Command*> cmds;
1394 playlist->rdiff (cmds);
1395 _session->add_commands (cmds);
1396 _session->add_command (new StatefulDiffCommand (playlist));
1397 what_we_got->release ();
1404 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1410 boost::shared_ptr<Playlist> pl = playlist ();
1411 PlaylistSelection::iterator p;
1413 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1415 if (p == selection.playlists.end()) {
1419 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1421 if (track()->speed() != 1.0f) {
1422 pos = session_frame_to_track_frame (pos, track()->speed());
1423 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1426 pl->clear_changes ();
1427 pl->paste (*p, pos, times);
1428 _session->add_command (new StatefulDiffCommand (pl));
1434 struct PlaylistSorter {
1435 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1436 return a->sort_id() < b->sort_id();
1441 RouteTimeAxisView::build_playlist_menu ()
1443 using namespace Menu_Helpers;
1449 delete playlist_action_menu;
1450 playlist_action_menu = new Menu;
1451 playlist_action_menu->set_name ("ArdourContextMenu");
1453 MenuList& playlist_items = playlist_action_menu->items();
1454 playlist_action_menu->set_name ("ArdourContextMenu");
1455 playlist_items.clear();
1457 RadioMenuItem::Group playlist_group;
1458 boost::shared_ptr<Track> tr = track ();
1460 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1462 /* sort the playlists */
1464 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1466 /* add the playlists to the menu */
1467 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1468 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1469 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1470 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1472 if (tr->playlist()->id() == (*i)->id()) {
1478 playlist_items.push_back (SeparatorElem());
1479 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1480 playlist_items.push_back (SeparatorElem());
1482 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1483 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1484 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1487 // Use a label which tells the user what is happening
1488 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1489 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1493 playlist_items.push_back (SeparatorElem());
1494 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1495 playlist_items.push_back (SeparatorElem());
1497 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1501 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1503 assert (is_track());
1505 // exit if we were triggered by deactivating the old playlist
1506 if (!item->get_active()) {
1510 boost::shared_ptr<Playlist> pl (wpl.lock());
1516 if (track()->playlist() == pl) {
1517 // exit when use_playlist is called by the creation of the playlist menu
1518 // or the playlist choice is unchanged
1522 track()->use_playlist (pl);
1524 RouteGroup* rg = route_group();
1526 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1527 std::string group_string = "." + rg->name() + ".";
1529 std::string take_name = pl->name();
1530 std::string::size_type idx = take_name.find(group_string);
1532 if (idx == std::string::npos)
1535 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1537 boost::shared_ptr<RouteList> rl (rg->route_list());
1539 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1540 if ((*i) == this->route()) {
1544 std::string playlist_name = (*i)->name()+group_string+take_name;
1546 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1551 if (track->freeze_state() == Track::Frozen) {
1552 /* Don't change playlists of frozen tracks */
1556 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1558 // No playlist for this track for this take yet, make it
1559 track->use_new_playlist();
1560 track->playlist()->set_name(playlist_name);
1562 track->use_playlist(ipl);
1569 RouteTimeAxisView::update_playlist_tip ()
1571 RouteGroup* rg = route_group ();
1572 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1573 string group_string = "." + rg->name() + ".";
1575 string take_name = track()->playlist()->name();
1576 string::size_type idx = take_name.find(group_string);
1578 if (idx != string::npos) {
1579 /* find the bit containing the take number / name */
1580 take_name = take_name.substr (idx + group_string.length());
1582 /* set the playlist button tooltip to the take name */
1583 ARDOUR_UI::instance()->set_tip (
1585 string_compose(_("Take: %1.%2"),
1586 Glib::Markup::escape_text(rg->name()),
1587 Glib::Markup::escape_text(take_name))
1594 /* set the playlist button tooltip to the playlist name */
1595 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1600 RouteTimeAxisView::show_playlist_selector ()
1602 _editor.playlist_selector().show_for (this);
1606 RouteTimeAxisView::map_frozen ()
1612 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1614 switch (track()->freeze_state()) {
1616 playlist_button.set_sensitive (false);
1617 rec_enable_button->set_sensitive (false);
1620 playlist_button.set_sensitive (true);
1621 rec_enable_button->set_sensitive (true);
1627 RouteTimeAxisView::color_handler ()
1629 //case cTimeStretchOutline:
1630 if (timestretch_rect) {
1631 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1633 //case cTimeStretchFill:
1634 if (timestretch_rect) {
1635 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1641 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1642 * Will add track if necessary.
1645 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1647 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1648 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1651 /* it doesn't exist yet, so we don't care about the button state: just add it */
1652 create_automation_child (param, true);
1655 bool yn = menu->get_active();
1656 bool changed = false;
1658 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1660 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1661 will have done that for us.
1664 if (changed && !no_redraw) {
1672 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1674 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1680 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1682 if (menu && !_hidden) {
1683 ignore_toggle = true;
1684 menu->set_active (false);
1685 ignore_toggle = false;
1688 if (_route && !no_redraw) {
1695 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1697 if (apply_to_selection) {
1698 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1702 /* Show our automation */
1704 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1705 i->second->set_marked_for_display (true);
1707 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1710 menu->set_active(true);
1715 /* Show processor automation */
1717 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1718 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1719 if ((*ii)->view == 0) {
1720 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1723 (*ii)->menu_item->set_active (true);
1736 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1738 if (apply_to_selection) {
1739 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1743 /* Show our automation */
1745 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1746 if (i->second->has_automation()) {
1747 i->second->set_marked_for_display (true);
1749 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1751 menu->set_active(true);
1756 /* Show processor automation */
1758 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1759 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1760 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1761 (*ii)->menu_item->set_active (true);
1773 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1775 if (apply_to_selection) {
1776 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1780 /* Hide our automation */
1782 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1783 i->second->set_marked_for_display (false);
1785 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1788 menu->set_active (false);
1792 /* Hide processor automation */
1794 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1795 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1796 (*ii)->menu_item->set_active (false);
1807 RouteTimeAxisView::region_view_added (RegionView* rv)
1809 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1810 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1811 boost::shared_ptr<AutomationTimeAxisView> atv;
1813 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1818 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1819 (*i)->add_ghost(rv);
1823 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1825 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1831 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1833 parent.remove_processor_automation_node (this);
1837 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1840 remove_child (pan->view);
1844 RouteTimeAxisView::ProcessorAutomationNode*
1845 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1847 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1849 if ((*i)->processor == processor) {
1851 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1852 if ((*ii)->what == what) {
1862 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1864 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1867 ProcessorAutomationNode* pan;
1869 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1870 /* session state may never have been saved with new plugin */
1871 error << _("programming error: ")
1872 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1873 processor->name(), what.type(), (int) what.channel(), what.id() )
1883 boost::shared_ptr<AutomationControl> control
1884 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1886 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1887 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1888 _editor, *this, false, parent_canvas,
1889 processor->describe_parameter (what), processor->name()));
1891 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1893 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1896 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1901 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1904 pan->menu_item->set_active (false);
1913 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1915 boost::shared_ptr<Processor> processor (p.lock ());
1917 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1918 /* The Amp processor is a special case and is dealt with separately */
1922 set<Evoral::Parameter> existing;
1924 processor->what_has_data (existing);
1926 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1928 Evoral::Parameter param (*i);
1929 boost::shared_ptr<AutomationLine> al;
1931 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1934 add_processor_automation_curve (processor, param);
1940 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1942 using namespace Menu_Helpers;
1946 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1948 _automation_tracks[param] = track;
1950 /* existing state overrides "show" argument */
1951 string s = track->gui_property ("visible");
1953 show = string_is_affirmative (s);
1956 /* this might or might not change the visibility status, so don't rely on it */
1957 track->set_marked_for_display (show);
1959 if (show && !no_redraw) {
1963 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1964 /* MIDI-related parameters are always in the menu, there's no
1965 reason to rebuild the menu just because we added a automation
1966 lane for one of them. But if we add a non-MIDI automation
1967 lane, then we need to invalidate the display menu.
1969 delete display_menu;
1975 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1977 boost::shared_ptr<Processor> processor (p.lock ());
1979 if (!processor || !processor->display_to_user ()) {
1983 /* we use this override to veto the Amp processor from the plugin menu,
1984 as its automation lane can be accessed using the special "Fader" menu
1988 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1992 using namespace Menu_Helpers;
1993 ProcessorAutomationInfo *rai;
1994 list<ProcessorAutomationInfo*>::iterator x;
1996 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1998 if (automatable.empty()) {
2002 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2003 if ((*x)->processor == processor) {
2008 if (x == processor_automation.end()) {
2010 rai = new ProcessorAutomationInfo (processor);
2011 processor_automation.push_back (rai);
2019 /* any older menu was deleted at the top of processors_changed()
2020 when we cleared the subplugin menu.
2023 rai->menu = manage (new Menu);
2024 MenuList& items = rai->menu->items();
2025 rai->menu->set_name ("ArdourContextMenu");
2029 std::set<Evoral::Parameter> has_visible_automation;
2030 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2032 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2034 ProcessorAutomationNode* pan;
2035 CheckMenuItem* mitem;
2037 string name = processor->describe_parameter (*i);
2039 items.push_back (CheckMenuElem (name));
2040 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2042 _subplugin_menu_map[*i] = mitem;
2044 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2045 mitem->set_active(true);
2048 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2052 pan = new ProcessorAutomationNode (*i, mitem, *this);
2054 rai->lines.push_back (pan);
2058 pan->menu_item = mitem;
2062 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2065 /* add the menu for this processor, because the subplugin
2066 menu is always cleared at the top of processors_changed().
2067 this is the result of some poor design in gtkmm and/or
2071 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2076 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2077 RouteTimeAxisView::ProcessorAutomationNode* pan)
2079 bool showit = pan->menu_item->get_active();
2080 bool redraw = false;
2082 if (pan->view == 0 && showit) {
2083 add_processor_automation_curve (rai->processor, pan->what);
2087 if (pan->view && pan->view->set_marked_for_display (showit)) {
2091 if (redraw && !no_redraw) {
2097 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2099 if (c.type == RouteProcessorChange::MeterPointChange) {
2100 /* nothing to do if only the meter point has changed */
2104 using namespace Menu_Helpers;
2106 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2107 (*i)->valid = false;
2110 setup_processor_menu_and_curves ();
2112 bool deleted_processor_automation = false;
2114 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2116 list<ProcessorAutomationInfo*>::iterator tmp;
2124 processor_automation.erase (i);
2125 deleted_processor_automation = true;
2132 if (deleted_processor_automation && !no_redraw) {
2137 boost::shared_ptr<AutomationLine>
2138 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2140 ProcessorAutomationNode* pan;
2142 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2148 return boost::shared_ptr<AutomationLine>();
2152 RouteTimeAxisView::reset_processor_automation_curves ()
2154 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2160 RouteTimeAxisView::can_edit_name () const
2162 /* we do not allow track name changes if it is record enabled
2164 return !_route->record_enabled();
2168 RouteTimeAxisView::update_rec_display ()
2170 RouteUI::update_rec_display ();
2172 if (_route->record_enabled()) {
2182 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2184 if (_ignore_set_layer_display) {
2188 if (apply_to_selection) {
2189 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2193 _view->set_layer_display (d);
2196 set_gui_property (X_("layer-display"), enum_2_string (d));
2201 RouteTimeAxisView::layer_display () const
2204 return _view->layer_display ();
2207 /* we don't know, since we don't have a _view, so just return something */
2213 boost::shared_ptr<AutomationTimeAxisView>
2214 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2216 AutomationTracks::iterator i = _automation_tracks.find(param);
2217 if (i != _automation_tracks.end()) {
2220 return boost::shared_ptr<AutomationTimeAxisView>();
2225 RouteTimeAxisView::fast_update ()
2227 gm.get_level_meter().update_meters ();
2231 RouteTimeAxisView::hide_meter ()
2234 gm.get_level_meter().hide_meters ();
2238 RouteTimeAxisView::show_meter ()
2244 RouteTimeAxisView::reset_meter ()
2246 if (Config->get_show_track_meters()) {
2247 gm.get_level_meter().setup_meters (height-5);
2254 RouteTimeAxisView::clear_meter ()
2256 gm.get_level_meter().clear_meters ();
2260 RouteTimeAxisView::meter_changed ()
2262 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2267 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2273 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2275 using namespace Menu_Helpers;
2277 if (!_underlay_streams.empty()) {
2278 MenuList& parent_items = parent_menu->items();
2279 Menu* gs_menu = manage (new Menu);
2280 gs_menu->set_name ("ArdourContextMenu");
2281 MenuList& gs_items = gs_menu->items();
2283 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2285 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2286 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2287 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2293 RouteTimeAxisView::set_underlay_state()
2295 if (!underlay_xml_node) {
2299 XMLNodeList nlist = underlay_xml_node->children();
2300 XMLNodeConstIterator niter;
2301 XMLNode *child_node;
2303 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2304 child_node = *niter;
2306 if (child_node->name() != "Underlay") {
2310 XMLProperty* prop = child_node->property ("id");
2312 PBD::ID id (prop->value());
2314 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2317 add_underlay(v->view(), false);
2326 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2332 RouteTimeAxisView& other = v->trackview();
2334 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2335 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2336 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2340 _underlay_streams.push_back(v);
2341 other._underlay_mirrors.push_back(this);
2343 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2345 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2347 if (!underlay_xml_node) {
2348 underlay_xml_node = xml_node->add_child("Underlays");
2351 XMLNode* node = underlay_xml_node->add_child("Underlay");
2352 XMLProperty* prop = node->add_property("id");
2353 prop->set_value(v->trackview().route()->id().to_s());
2360 RouteTimeAxisView::remove_underlay (StreamView* v)
2366 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2367 RouteTimeAxisView& other = v->trackview();
2369 if (it != _underlay_streams.end()) {
2370 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2372 if (gm == other._underlay_mirrors.end()) {
2373 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2377 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2379 _underlay_streams.erase(it);
2380 other._underlay_mirrors.erase(gm);
2382 if (underlay_xml_node) {
2383 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2389 RouteTimeAxisView::set_button_names ()
2391 if (_route && _route->solo_safe()) {
2392 solo_button->remove ();
2393 if (solo_safe_pixbuf == 0) {
2394 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2396 solo_button->set_image (solo_safe_pixbuf);
2397 solo_button->set_text (string());
2399 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2400 if (Config->get_solo_control_is_listen_control()) {
2401 switch (Config->get_listen_position()) {
2402 case AfterFaderListen:
2403 solo_button->set_text (_("A"));
2404 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2406 case PreFaderListen:
2407 solo_button->set_text (_("P"));
2408 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2412 solo_button->set_text (_("s"));
2413 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2416 mute_button->set_text (_("m"));
2420 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2422 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2423 if (i != _main_automation_menu_map.end()) {
2427 i = _subplugin_menu_map.find (param);
2428 if (i != _subplugin_menu_map.end()) {
2436 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2438 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2440 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2444 gain_track.reset (new AutomationTimeAxisView (_session,
2445 _route, _route->amp(), c, param,
2450 _route->amp()->describe_parameter(param)));
2453 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2456 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2460 void add_region_to_list (RegionView* rv, RegionList* l)
2462 l->push_back (rv->region());
2466 RouteTimeAxisView::combine_regions ()
2468 /* as of may 2011, we do not offer uncombine for MIDI tracks
2471 if (!is_audio_track()) {
2479 RegionList selected_regions;
2480 boost::shared_ptr<Playlist> playlist = track()->playlist();
2482 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2484 if (selected_regions.size() < 2) {
2488 playlist->clear_changes ();
2489 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2491 _session->add_command (new StatefulDiffCommand (playlist));
2492 /* make the new region be selected */
2494 return _view->find_view (compound_region);
2498 RouteTimeAxisView::uncombine_regions ()
2500 /* as of may 2011, we do not offer uncombine for MIDI tracks
2502 if (!is_audio_track()) {
2510 RegionList selected_regions;
2511 boost::shared_ptr<Playlist> playlist = track()->playlist();
2513 /* have to grab selected regions first because the uncombine is going
2514 * to change that in the middle of the list traverse
2517 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2519 playlist->clear_changes ();
2521 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2522 playlist->uncombine (*i);
2525 _session->add_command (new StatefulDiffCommand (playlist));
2529 RouteTimeAxisView::state_id() const
2531 return string_compose ("rtav %1", _route->id().to_s());
2536 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2538 TimeAxisView::remove_child (c);
2540 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2542 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2543 if (i->second == a) {
2544 _automation_tracks.erase (i);