2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("g"))
100 , playlist_button (_("p"))
101 , automation_button (_("a"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 125, 18)
109 , _ignore_set_layer_display (false)
111 number_label.set_corner_radius(2);
112 number_label.set_name("tracknumber label");
113 number_label.set_alignment(.5, .5);
115 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
119 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 RouteUI::set_route (rt);
123 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
124 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
125 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
128 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
131 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
132 gm.get_level_meter().set_no_show_all();
133 gm.get_level_meter().setup_meters(50, meter_width);
134 gm.update_gain_sensitive ();
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_auditioner()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("route button");
160 playlist_button.set_name ("route button");
161 automation_button.set_name ("route button");
163 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
164 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
165 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 switch (track()->mode()) {
173 case ARDOUR::NonLayered:
174 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
176 case ARDOUR::Destructive:
177 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
181 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
183 if (is_midi_track()) {
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
201 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
202 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
203 controls_hbox.pack_start(*mtrbox, false, false, 4);
206 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
207 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
208 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
209 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
211 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 if (!_route->is_master()) {
214 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
217 if (!ARDOUR::Profile->get_trx()) {
218 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
219 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
222 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
223 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
224 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
226 if (is_midi_track()) {
227 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
229 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
232 update_track_number_visibility();
235 if (!ARDOUR::Profile->get_trx()) {
236 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
239 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
240 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
245 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
246 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
250 str = gui_property ("layer-display");
252 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
255 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
256 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
258 /* pick up the correct freeze state */
263 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
264 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
266 PropertyList* plist = new PropertyList();
268 plist->add (ARDOUR::Properties::mute, true);
269 plist->add (ARDOUR::Properties::solo, true);
271 route_group_menu = new RouteGroupMenu (_session, plist);
273 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
275 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
278 RouteTimeAxisView::~RouteTimeAxisView ()
280 CatchDeletion (this);
282 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
286 delete playlist_action_menu;
287 playlist_action_menu = 0;
292 _automation_tracks.clear ();
294 delete route_group_menu;
298 RouteTimeAxisView::post_construct ()
300 /* map current state of the route */
302 update_diskstream_display ();
303 setup_processor_menu_and_curves ();
304 reset_processor_automation_curves ();
307 /** Set up the processor menu for the current set of processors, and
308 * display automation curves for any parameters which have data.
311 RouteTimeAxisView::setup_processor_menu_and_curves ()
313 _subplugin_menu_map.clear ();
314 subplugin_menu.items().clear ();
315 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
316 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
320 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
322 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
323 if (_route->route_group()) {
324 _route->route_group()->remove (_route);
330 r.push_back (route ());
332 route_group_menu->build (r);
333 route_group_menu->menu()->popup (ev->button, ev->time);
339 RouteTimeAxisView::playlist_changed ()
345 RouteTimeAxisView::label_view ()
347 string x = _route->name ();
348 if (x != name_label.get_text ()) {
349 name_label.set_text (x);
351 const int64_t track_number = _route->track_number ();
352 if (track_number == 0) {
353 number_label.set_text ("");
355 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
360 RouteTimeAxisView::update_track_number_visibility ()
362 bool show_label = _session->config.get_track_name_number();
364 if (_route && _route->is_master()) {
368 //if (show_label == number_label.is_visible()) { return; }
369 if (number_label.get_parent()) {
370 controls_table.remove (number_label);
372 if (name_hbox.get_parent()) {
373 controls_table.remove (name_hbox);
376 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::FILL|Gtk::EXPAND, 3, 0);
377 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
378 number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
380 number_label.show ();
382 controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
384 number_label.hide ();
389 RouteTimeAxisView::parameter_changed (string const & p)
391 if (p == "track-name-number") {
392 update_track_number_visibility();
397 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
399 if (what_changed.contains (ARDOUR::Properties::name)) {
405 RouteTimeAxisView::take_name_changed (void *src)
413 RouteTimeAxisView::playlist_click ()
415 build_playlist_menu ();
416 conditionally_add_to_selection ();
417 playlist_action_menu->popup (1, gtk_get_current_event_time());
421 RouteTimeAxisView::automation_click ()
423 conditionally_add_to_selection ();
424 build_automation_action_menu (false);
425 automation_action_menu->popup (1, gtk_get_current_event_time());
429 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
431 using namespace Menu_Helpers;
433 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
434 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
437 detach_menu (subplugin_menu);
439 _main_automation_menu_map.clear ();
440 delete automation_action_menu;
441 automation_action_menu = new Menu;
443 MenuList& items = automation_action_menu->items();
445 automation_action_menu->set_name ("ArdourContextMenu");
447 items.push_back (MenuElem (_("Show All Automation"),
448 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
450 items.push_back (MenuElem (_("Show Existing Automation"),
451 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
453 items.push_back (MenuElem (_("Hide All Automation"),
454 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
456 /* Attach the plugin submenu. It may have previously been used elsewhere,
457 so it was detached above
460 if (!subplugin_menu.items().empty()) {
461 items.push_back (SeparatorElem ());
462 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
463 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
468 RouteTimeAxisView::build_display_menu ()
470 using namespace Menu_Helpers;
474 TimeAxisView::build_display_menu ();
476 /* now fill it with our stuff */
478 MenuList& items = display_menu->items();
479 display_menu->set_name ("ArdourContextMenu");
481 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
484 detach_menu (*_size_menu);
487 items.push_back (MenuElem (_("Height"), *_size_menu));
489 items.push_back (SeparatorElem());
491 if (!Profile->get_sae()) {
492 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
493 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
494 items.push_back (SeparatorElem());
497 // Hook for derived classes to add type specific stuff
498 append_extra_display_menu_items ();
502 Menu* layers_menu = manage (new Menu);
503 MenuList &layers_items = layers_menu->items();
504 layers_menu->set_name("ArdourContextMenu");
506 RadioMenuItem::Group layers_group;
508 /* Find out how many overlaid/stacked tracks we have in the selection */
512 TrackSelection const & s = _editor.get_selection().tracks;
513 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
514 StreamView* v = (*i)->view ();
519 switch (v->layer_display ()) {
530 /* We're not connecting to signal_toggled() here; in the case where these two items are
531 set to be in the `inconsistent' state, it seems that one or other will end up active
532 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
533 select the active one, no toggled signal is emitted so nothing happens.
536 _ignore_set_layer_display = true;
538 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
539 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
540 i->set_active (overlaid != 0 && stacked == 0);
541 i->set_inconsistent (overlaid != 0 && stacked != 0);
542 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
544 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
545 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
546 i->set_active (overlaid == 0 && stacked != 0);
547 i->set_inconsistent (overlaid != 0 && stacked != 0);
548 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
550 _ignore_set_layer_display = false;
552 items.push_back (MenuElem (_("Layers"), *layers_menu));
554 if (!Profile->get_sae()) {
556 Menu* alignment_menu = manage (new Menu);
557 MenuList& alignment_items = alignment_menu->items();
558 alignment_menu->set_name ("ArdourContextMenu");
560 RadioMenuItem::Group align_group;
562 /* Same verbose hacks as for the layering options above */
568 boost::shared_ptr<Track> first_track;
570 TrackSelection const & s = _editor.get_selection().tracks;
571 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
572 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
573 if (!r || !r->is_track ()) {
578 first_track = r->track();
581 switch (r->track()->alignment_choice()) {
585 switch (r->track()->alignment_style()) {
586 case ExistingMaterial:
594 case UseExistingMaterial:
610 inconsistent = false;
619 if (!inconsistent && first_track) {
621 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
622 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
623 i->set_active (automatic != 0 && existing == 0 && capture == 0);
624 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
626 switch (first_track->alignment_choice()) {
628 switch (first_track->alignment_style()) {
629 case ExistingMaterial:
630 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
633 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
641 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
642 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
643 i->set_active (existing != 0 && capture == 0 && automatic == 0);
644 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
646 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
647 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
648 i->set_active (existing == 0 && capture != 0 && automatic == 0);
649 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
651 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
657 Menu* mode_menu = manage (new Menu);
658 MenuList& mode_items = mode_menu->items ();
659 mode_menu->set_name ("ArdourContextMenu");
661 RadioMenuItem::Group mode_group;
667 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
668 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
669 if (!r || !r->is_track ()) {
673 switch (r->track()->mode()) {
686 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
687 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
688 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
689 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
690 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
692 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
693 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
694 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
695 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
696 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
698 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
699 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
701 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
702 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
704 items.push_back (MenuElem (_("Mode"), *mode_menu));
708 items.push_back (SeparatorElem());
710 build_playlist_menu ();
711 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
712 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
715 route_group_menu->detach ();
718 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
719 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
721 r.push_back (rtv->route ());
726 r.push_back (route ());
729 route_group_menu->build (r);
730 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
732 build_automation_action_menu (true);
733 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
735 items.push_back (SeparatorElem());
739 TrackSelection const & s = _editor.get_selection().tracks;
740 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
741 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
746 if (r->route()->active()) {
753 items.push_back (CheckMenuElem (_("Active")));
754 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
755 bool click_sets_active = true;
756 if (active > 0 && inactive == 0) {
757 i->set_active (true);
758 click_sets_active = false;
759 } else if (active > 0 && inactive > 0) {
760 i->set_inconsistent (true);
762 i->set_sensitive(! _session->transport_rolling());
763 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
765 items.push_back (SeparatorElem());
766 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
767 if (!Profile->get_sae()) {
768 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
770 items.push_front (SeparatorElem());
771 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
776 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
778 if (apply_to_selection) {
779 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
782 bool needs_bounce = false;
784 if (!track()->can_use_mode (mode, needs_bounce)) {
790 cerr << "would bounce this one\n";
795 track()->set_mode (mode);
797 rec_enable_button->remove ();
800 case ARDOUR::NonLayered:
802 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
803 rec_enable_button->set_text (string());
805 case ARDOUR::Destructive:
806 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
807 rec_enable_button->set_text (string());
811 rec_enable_button->show_all ();
816 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
818 TimeAxisView::show_timestretch (start, end, layers, layer);
828 /* check that the time selection was made in our route, or our route group.
829 remember that route_group() == 0 implies the route is *not* in a edit group.
832 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
833 /* this doesn't apply to us */
837 /* ignore it if our edit group is not active */
839 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
844 if (timestretch_rect == 0) {
845 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
846 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
847 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
850 timestretch_rect->show ();
851 timestretch_rect->raise_to_top ();
853 double const x1 = start / _editor.get_current_zoom();
854 double const x2 = (end - 1) / _editor.get_current_zoom();
856 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
857 x2, current_height() * (layers - layer) / layers));
861 RouteTimeAxisView::hide_timestretch ()
863 TimeAxisView::hide_timestretch ();
865 if (timestretch_rect) {
866 timestretch_rect->hide ();
871 RouteTimeAxisView::show_selection (TimeSelection& ts)
875 /* ignore it if our edit group is not active or if the selection was started
876 in some other track or route group (remember that route_group() == 0 means
877 that the track is not in an route group).
880 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
881 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
887 TimeAxisView::show_selection (ts);
891 RouteTimeAxisView::set_height (uint32_t h)
894 bool height_changed = (height == 0) || (h != height);
897 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
900 gm.get_level_meter().setup_meters (gmlen, meter_width);
902 TimeAxisView::set_height (h);
905 _view->set_height ((double) current_height());
908 if (height >= preset_height (HeightNormal)) {
912 gm.get_gain_slider().show();
914 if (!_route || _route->is_monitor()) {
919 if (rec_enable_button)
920 rec_enable_button->show();
922 route_group_button.show();
923 automation_button.show();
925 if (is_track() && track()->mode() == ARDOUR::Normal) {
926 playlist_button.show();
933 gm.get_gain_slider().hide();
935 if (!_route || _route->is_monitor()) {
940 if (rec_enable_button)
941 rec_enable_button->show();
943 route_group_button.hide ();
944 automation_button.hide ();
946 if (is_track() && track()->mode() == ARDOUR::Normal) {
947 playlist_button.hide ();
952 if (height_changed && !no_redraw) {
953 /* only emit the signal if the height really changed */
959 RouteTimeAxisView::route_color_changed ()
962 _view->apply_color (color(), StreamView::RegionColor);
967 RouteTimeAxisView::reset_samples_per_pixel ()
969 set_samples_per_pixel (_editor.get_current_zoom());
973 RouteTimeAxisView::set_samples_per_pixel (double fpp)
978 speed = track()->speed();
982 _view->set_samples_per_pixel (fpp * speed);
985 TimeAxisView::set_samples_per_pixel (fpp * speed);
989 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
991 if (!mitem->get_active()) {
992 /* this is one of the two calls made when these radio menu items change status. this one
993 is for the item that became inactive, and we want to ignore it.
998 if (apply_to_selection) {
999 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1002 track()->set_align_choice (choice);
1008 RouteTimeAxisView::rename_current_playlist ()
1010 ArdourPrompter prompter (true);
1013 boost::shared_ptr<Track> tr = track();
1014 if (!tr || tr->destructive()) {
1018 boost::shared_ptr<Playlist> pl = tr->playlist();
1023 prompter.set_title (_("Rename Playlist"));
1024 prompter.set_prompt (_("New name for playlist:"));
1025 prompter.set_initial_text (pl->name());
1026 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1027 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1029 switch (prompter.run ()) {
1030 case Gtk::RESPONSE_ACCEPT:
1031 prompter.get_result (name);
1032 if (name.length()) {
1033 pl->set_name (name);
1043 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1045 std::string ret (basename);
1047 std::string const group_string = "." + route_group()->name() + ".";
1049 // iterate through all playlists
1051 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1052 std::string tmp = (*i)->name();
1054 std::string::size_type idx = tmp.find(group_string);
1055 // find those which belong to this group
1056 if (idx != string::npos) {
1057 tmp = tmp.substr(idx + group_string.length());
1059 // and find the largest current number
1061 if (x > maxnumber) {
1070 snprintf (buf, sizeof(buf), "%d", maxnumber);
1072 ret = this->name() + "." + route_group()->name () + "." + buf;
1078 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1082 boost::shared_ptr<Track> tr = track ();
1083 if (!tr || tr->destructive()) {
1087 boost::shared_ptr<const Playlist> pl = tr->playlist();
1094 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1095 name = resolve_new_group_playlist_name(name, playlists_before_op);
1098 while (_session->playlists->by_name(name)) {
1099 name = Playlist::bump_name (name, *_session);
1102 // TODO: The prompter "new" button should be de-activated if the user
1103 // specifies a playlist name which already exists in the session.
1107 ArdourPrompter prompter (true);
1109 prompter.set_title (_("New Copy Playlist"));
1110 prompter.set_prompt (_("Name for new playlist:"));
1111 prompter.set_initial_text (name);
1112 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1113 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1114 prompter.show_all ();
1116 switch (prompter.run ()) {
1117 case Gtk::RESPONSE_ACCEPT:
1118 prompter.get_result (name);
1126 if (name.length()) {
1127 tr->use_copy_playlist ();
1128 tr->playlist()->set_name (name);
1133 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1137 boost::shared_ptr<Track> tr = track ();
1138 if (!tr || tr->destructive()) {
1142 boost::shared_ptr<const Playlist> pl = tr->playlist();
1149 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1150 name = resolve_new_group_playlist_name(name,playlists_before_op);
1153 while (_session->playlists->by_name(name)) {
1154 name = Playlist::bump_name (name, *_session);
1160 ArdourPrompter prompter (true);
1162 prompter.set_title (_("New Playlist"));
1163 prompter.set_prompt (_("Name for new playlist:"));
1164 prompter.set_initial_text (name);
1165 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1166 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1168 switch (prompter.run ()) {
1169 case Gtk::RESPONSE_ACCEPT:
1170 prompter.get_result (name);
1178 if (name.length()) {
1179 tr->use_new_playlist ();
1180 tr->playlist()->set_name (name);
1185 RouteTimeAxisView::clear_playlist ()
1187 boost::shared_ptr<Track> tr = track ();
1188 if (!tr || tr->destructive()) {
1192 boost::shared_ptr<Playlist> pl = tr->playlist();
1197 _editor.clear_playlist (pl);
1201 RouteTimeAxisView::speed_changed ()
1203 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1207 RouteTimeAxisView::update_diskstream_display ()
1217 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1219 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1221 /* special case: select/deselect all tracks */
1222 if (_editor.get_selection().selected (this)) {
1223 _editor.get_selection().clear_tracks ();
1225 _editor.select_all_tracks ();
1231 switch (ArdourKeyboard::selection_type (ev->state)) {
1232 case Selection::Toggle:
1233 _editor.get_selection().toggle (this);
1236 case Selection::Set:
1237 _editor.get_selection().set (this);
1240 case Selection::Extend:
1241 _editor.extend_selection_to_track (*this);
1244 case Selection::Add:
1245 _editor.get_selection().add (this);
1251 RouteTimeAxisView::set_selected_points (PointSelection& points)
1253 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1254 (*i)->set_selected_points (points);
1259 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1262 _view->set_selected_regionviews (regions);
1266 /** Add the selectable things that we have to a list.
1267 * @param results List to add things to.
1270 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1275 speed = track()->speed();
1278 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1279 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1281 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1282 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1285 /* pick up visible automation tracks */
1287 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1288 if (!(*i)->hidden()) {
1289 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1295 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1298 _view->get_inverted_selectables (sel, results);
1301 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1302 if (!(*i)->hidden()) {
1303 (*i)->get_inverted_selectables (sel, results);
1311 RouteTimeAxisView::route_group () const
1313 return _route->route_group();
1317 RouteTimeAxisView::name() const
1319 return _route->name();
1322 boost::shared_ptr<Playlist>
1323 RouteTimeAxisView::playlist () const
1325 boost::shared_ptr<Track> tr;
1327 if ((tr = track()) != 0) {
1328 return tr->playlist();
1330 return boost::shared_ptr<Playlist> ();
1335 RouteTimeAxisView::name_entry_changed ()
1337 TimeAxisView::name_entry_changed ();
1339 string x = name_entry->get_text ();
1341 if (x == _route->name()) {
1345 strip_whitespace_edges (x);
1347 if (x.length() == 0) {
1348 name_entry->set_text (_route->name());
1352 if (_session->route_name_internal (x)) {
1353 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1355 name_entry->grab_focus ();
1356 } else if (RouteUI::verify_new_route_name (x)) {
1357 _route->set_name (x);
1359 name_entry->grab_focus ();
1363 boost::shared_ptr<Region>
1364 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1366 boost::shared_ptr<Playlist> pl = playlist ();
1369 return pl->find_next_region (pos, point, dir);
1372 return boost::shared_ptr<Region> ();
1376 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1378 boost::shared_ptr<Playlist> pl = playlist ();
1381 return pl->find_next_region_boundary (pos, dir);
1388 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1390 boost::shared_ptr<Playlist> what_we_got;
1391 boost::shared_ptr<Track> tr = track ();
1392 boost::shared_ptr<Playlist> playlist;
1395 /* route is a bus, not a track */
1399 playlist = tr->playlist();
1401 TimeSelection time (selection.time);
1402 float const speed = tr->speed();
1403 if (speed != 1.0f) {
1404 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1405 (*i).start = session_frame_to_track_frame((*i).start, speed);
1406 (*i).end = session_frame_to_track_frame((*i).end, speed);
1410 playlist->clear_changes ();
1411 playlist->clear_owned_changes ();
1415 if (playlist->cut (time) != 0) {
1416 vector<Command*> cmds;
1417 playlist->rdiff (cmds);
1418 _session->add_commands (cmds);
1420 _session->add_command (new StatefulDiffCommand (playlist));
1425 if ((what_we_got = playlist->cut (time)) != 0) {
1426 _editor.get_cut_buffer().add (what_we_got);
1427 vector<Command*> cmds;
1428 playlist->rdiff (cmds);
1429 _session->add_commands (cmds);
1431 _session->add_command (new StatefulDiffCommand (playlist));
1435 if ((what_we_got = playlist->copy (time)) != 0) {
1436 _editor.get_cut_buffer().add (what_we_got);
1441 if ((what_we_got = playlist->cut (time)) != 0) {
1443 vector<Command*> cmds;
1444 playlist->rdiff (cmds);
1445 _session->add_commands (cmds);
1446 _session->add_command (new StatefulDiffCommand (playlist));
1447 what_we_got->release ();
1454 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1460 boost::shared_ptr<Playlist> pl = playlist ();
1461 PlaylistSelection::iterator p;
1463 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1465 if (p == selection.playlists.end()) {
1469 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1471 if (track()->speed() != 1.0f) {
1472 pos = session_frame_to_track_frame (pos, track()->speed());
1473 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1476 pl->clear_changes ();
1477 pl->paste (*p, pos, times);
1478 _session->add_command (new StatefulDiffCommand (pl));
1484 struct PlaylistSorter {
1485 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1486 return a->sort_id() < b->sort_id();
1491 RouteTimeAxisView::build_playlist_menu ()
1493 using namespace Menu_Helpers;
1499 delete playlist_action_menu;
1500 playlist_action_menu = new Menu;
1501 playlist_action_menu->set_name ("ArdourContextMenu");
1503 MenuList& playlist_items = playlist_action_menu->items();
1504 playlist_action_menu->set_name ("ArdourContextMenu");
1505 playlist_items.clear();
1507 RadioMenuItem::Group playlist_group;
1508 boost::shared_ptr<Track> tr = track ();
1510 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1512 /* sort the playlists */
1514 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1516 /* add the playlists to the menu */
1517 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1518 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1519 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1520 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1522 if (tr->playlist()->id() == (*i)->id()) {
1528 playlist_items.push_back (SeparatorElem());
1529 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1530 playlist_items.push_back (SeparatorElem());
1532 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1533 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1534 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1537 // Use a label which tells the user what is happening
1538 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1539 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1543 playlist_items.push_back (SeparatorElem());
1544 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1545 playlist_items.push_back (SeparatorElem());
1547 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1551 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1553 assert (is_track());
1555 // exit if we were triggered by deactivating the old playlist
1556 if (!item->get_active()) {
1560 boost::shared_ptr<Playlist> pl (wpl.lock());
1566 if (track()->playlist() == pl) {
1567 // exit when use_playlist is called by the creation of the playlist menu
1568 // or the playlist choice is unchanged
1572 track()->use_playlist (pl);
1574 RouteGroup* rg = route_group();
1576 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1577 std::string group_string = "." + rg->name() + ".";
1579 std::string take_name = pl->name();
1580 std::string::size_type idx = take_name.find(group_string);
1582 if (idx == std::string::npos)
1585 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1587 boost::shared_ptr<RouteList> rl (rg->route_list());
1589 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1590 if ((*i) == this->route()) {
1594 std::string playlist_name = (*i)->name()+group_string+take_name;
1596 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1601 if (track->freeze_state() == Track::Frozen) {
1602 /* Don't change playlists of frozen tracks */
1606 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1608 // No playlist for this track for this take yet, make it
1609 track->use_new_playlist();
1610 track->playlist()->set_name(playlist_name);
1612 track->use_playlist(ipl);
1619 RouteTimeAxisView::update_playlist_tip ()
1621 RouteGroup* rg = route_group ();
1622 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1623 string group_string = "." + rg->name() + ".";
1625 string take_name = track()->playlist()->name();
1626 string::size_type idx = take_name.find(group_string);
1628 if (idx != string::npos) {
1629 /* find the bit containing the take number / name */
1630 take_name = take_name.substr (idx + group_string.length());
1632 /* set the playlist button tooltip to the take name */
1633 ARDOUR_UI::instance()->set_tip (
1635 string_compose(_("Take: %1.%2"),
1636 Glib::Markup::escape_text(rg->name()),
1637 Glib::Markup::escape_text(take_name))
1644 /* set the playlist button tooltip to the playlist name */
1645 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1650 RouteTimeAxisView::show_playlist_selector ()
1652 _editor.playlist_selector().show_for (this);
1656 RouteTimeAxisView::map_frozen ()
1662 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1664 switch (track()->freeze_state()) {
1666 playlist_button.set_sensitive (false);
1667 rec_enable_button->set_sensitive (false);
1670 playlist_button.set_sensitive (true);
1671 rec_enable_button->set_sensitive (true);
1677 RouteTimeAxisView::color_handler ()
1679 //case cTimeStretchOutline:
1680 if (timestretch_rect) {
1681 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1683 //case cTimeStretchFill:
1684 if (timestretch_rect) {
1685 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1691 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1692 * Will add track if necessary.
1695 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1697 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1698 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1701 /* it doesn't exist yet, so we don't care about the button state: just add it */
1702 create_automation_child (param, true);
1705 bool yn = menu->get_active();
1706 bool changed = false;
1708 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1710 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1711 will have done that for us.
1714 if (changed && !no_redraw) {
1722 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1724 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1730 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1732 if (menu && !_hidden) {
1733 ignore_toggle = true;
1734 menu->set_active (false);
1735 ignore_toggle = false;
1738 if (_route && !no_redraw) {
1745 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1747 if (apply_to_selection) {
1748 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1752 /* Show our automation */
1754 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1755 i->second->set_marked_for_display (true);
1757 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1760 menu->set_active(true);
1765 /* Show processor automation */
1767 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1768 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1769 if ((*ii)->view == 0) {
1770 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1773 (*ii)->menu_item->set_active (true);
1786 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1788 if (apply_to_selection) {
1789 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1793 /* Show our automation */
1795 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1796 if (i->second->has_automation()) {
1797 i->second->set_marked_for_display (true);
1799 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1801 menu->set_active(true);
1806 /* Show processor automation */
1808 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1809 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1810 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1811 (*ii)->menu_item->set_active (true);
1823 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1825 if (apply_to_selection) {
1826 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1830 /* Hide our automation */
1832 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1833 i->second->set_marked_for_display (false);
1835 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1838 menu->set_active (false);
1842 /* Hide processor automation */
1844 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1845 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1846 (*ii)->menu_item->set_active (false);
1857 RouteTimeAxisView::region_view_added (RegionView* rv)
1859 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1860 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1861 boost::shared_ptr<AutomationTimeAxisView> atv;
1863 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1868 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1869 (*i)->add_ghost(rv);
1873 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1875 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1881 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1883 parent.remove_processor_automation_node (this);
1887 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1890 remove_child (pan->view);
1894 RouteTimeAxisView::ProcessorAutomationNode*
1895 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1897 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1899 if ((*i)->processor == processor) {
1901 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1902 if ((*ii)->what == what) {
1912 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1914 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1917 ProcessorAutomationNode* pan;
1919 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1920 /* session state may never have been saved with new plugin */
1921 error << _("programming error: ")
1922 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1923 processor->name(), what.type(), (int) what.channel(), what.id() )
1933 boost::shared_ptr<AutomationControl> control
1934 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1936 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1937 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1938 _editor, *this, false, parent_canvas,
1939 processor->describe_parameter (what), processor->name()));
1941 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1943 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1946 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1951 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1954 pan->menu_item->set_active (false);
1963 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1965 boost::shared_ptr<Processor> processor (p.lock ());
1967 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1968 /* The Amp processor is a special case and is dealt with separately */
1972 set<Evoral::Parameter> existing;
1974 processor->what_has_data (existing);
1976 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1978 Evoral::Parameter param (*i);
1979 boost::shared_ptr<AutomationLine> al;
1981 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1984 add_processor_automation_curve (processor, param);
1990 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1992 using namespace Menu_Helpers;
1996 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1998 _automation_tracks[param] = track;
2000 /* existing state overrides "show" argument */
2001 string s = track->gui_property ("visible");
2003 show = string_is_affirmative (s);
2006 /* this might or might not change the visibility status, so don't rely on it */
2007 track->set_marked_for_display (show);
2009 if (show && !no_redraw) {
2013 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2014 /* MIDI-related parameters are always in the menu, there's no
2015 reason to rebuild the menu just because we added a automation
2016 lane for one of them. But if we add a non-MIDI automation
2017 lane, then we need to invalidate the display menu.
2019 delete display_menu;
2025 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2027 boost::shared_ptr<Processor> processor (p.lock ());
2029 if (!processor || !processor->display_to_user ()) {
2033 /* we use this override to veto the Amp processor from the plugin menu,
2034 as its automation lane can be accessed using the special "Fader" menu
2038 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2042 using namespace Menu_Helpers;
2043 ProcessorAutomationInfo *rai;
2044 list<ProcessorAutomationInfo*>::iterator x;
2046 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2048 if (automatable.empty()) {
2052 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2053 if ((*x)->processor == processor) {
2058 if (x == processor_automation.end()) {
2060 rai = new ProcessorAutomationInfo (processor);
2061 processor_automation.push_back (rai);
2069 /* any older menu was deleted at the top of processors_changed()
2070 when we cleared the subplugin menu.
2073 rai->menu = manage (new Menu);
2074 MenuList& items = rai->menu->items();
2075 rai->menu->set_name ("ArdourContextMenu");
2079 std::set<Evoral::Parameter> has_visible_automation;
2080 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2082 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2084 ProcessorAutomationNode* pan;
2085 Gtk::CheckMenuItem* mitem;
2087 string name = processor->describe_parameter (*i);
2089 items.push_back (CheckMenuElem (name));
2090 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2092 _subplugin_menu_map[*i] = mitem;
2094 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2095 mitem->set_active(true);
2098 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2102 pan = new ProcessorAutomationNode (*i, mitem, *this);
2104 rai->lines.push_back (pan);
2108 pan->menu_item = mitem;
2112 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2115 /* add the menu for this processor, because the subplugin
2116 menu is always cleared at the top of processors_changed().
2117 this is the result of some poor design in gtkmm and/or
2121 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2126 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2127 RouteTimeAxisView::ProcessorAutomationNode* pan)
2129 bool showit = pan->menu_item->get_active();
2130 bool redraw = false;
2132 if (pan->view == 0 && showit) {
2133 add_processor_automation_curve (rai->processor, pan->what);
2137 if (pan->view && pan->view->set_marked_for_display (showit)) {
2141 if (redraw && !no_redraw) {
2147 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2149 if (c.type == RouteProcessorChange::MeterPointChange) {
2150 /* nothing to do if only the meter point has changed */
2154 using namespace Menu_Helpers;
2156 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2157 (*i)->valid = false;
2160 setup_processor_menu_and_curves ();
2162 bool deleted_processor_automation = false;
2164 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2166 list<ProcessorAutomationInfo*>::iterator tmp;
2174 processor_automation.erase (i);
2175 deleted_processor_automation = true;
2182 if (deleted_processor_automation && !no_redraw) {
2187 boost::shared_ptr<AutomationLine>
2188 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2190 ProcessorAutomationNode* pan;
2192 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2198 return boost::shared_ptr<AutomationLine>();
2202 RouteTimeAxisView::reset_processor_automation_curves ()
2204 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2210 RouteTimeAxisView::can_edit_name () const
2212 /* we do not allow track name changes if it is record enabled
2214 return !_route->record_enabled();
2218 RouteTimeAxisView::update_rec_display ()
2220 RouteUI::update_rec_display ();
2224 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2226 if (_ignore_set_layer_display) {
2230 if (apply_to_selection) {
2231 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2235 _view->set_layer_display (d);
2238 set_gui_property (X_("layer-display"), enum_2_string (d));
2243 RouteTimeAxisView::layer_display () const
2246 return _view->layer_display ();
2249 /* we don't know, since we don't have a _view, so just return something */
2255 boost::shared_ptr<AutomationTimeAxisView>
2256 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2258 AutomationTracks::iterator i = _automation_tracks.find(param);
2259 if (i != _automation_tracks.end()) {
2262 return boost::shared_ptr<AutomationTimeAxisView>();
2267 RouteTimeAxisView::fast_update ()
2269 gm.get_level_meter().update_meters ();
2273 RouteTimeAxisView::hide_meter ()
2276 gm.get_level_meter().hide_meters ();
2280 RouteTimeAxisView::show_meter ()
2286 RouteTimeAxisView::reset_meter ()
2288 if (Config->get_show_track_meters()) {
2289 int meter_width = 3;
2290 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2293 gm.get_level_meter().setup_meters (height - 9, meter_width);
2300 RouteTimeAxisView::clear_meter ()
2302 gm.get_level_meter().clear_meters ();
2306 RouteTimeAxisView::meter_changed ()
2308 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2310 if (_route && !no_redraw) {
2313 // reset peak when meter point changes
2314 gm.reset_peak_display();
2318 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2321 if (_route && !no_redraw) {
2327 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2329 using namespace Menu_Helpers;
2331 if (!_underlay_streams.empty()) {
2332 MenuList& parent_items = parent_menu->items();
2333 Menu* gs_menu = manage (new Menu);
2334 gs_menu->set_name ("ArdourContextMenu");
2335 MenuList& gs_items = gs_menu->items();
2337 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2339 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2340 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2341 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2347 RouteTimeAxisView::set_underlay_state()
2349 if (!underlay_xml_node) {
2353 XMLNodeList nlist = underlay_xml_node->children();
2354 XMLNodeConstIterator niter;
2355 XMLNode *child_node;
2357 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2358 child_node = *niter;
2360 if (child_node->name() != "Underlay") {
2364 XMLProperty* prop = child_node->property ("id");
2366 PBD::ID id (prop->value());
2368 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2371 add_underlay(v->view(), false);
2380 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2386 RouteTimeAxisView& other = v->trackview();
2388 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2389 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2390 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2394 _underlay_streams.push_back(v);
2395 other._underlay_mirrors.push_back(this);
2397 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2399 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2401 if (!underlay_xml_node) {
2402 underlay_xml_node = xml_node->add_child("Underlays");
2405 XMLNode* node = underlay_xml_node->add_child("Underlay");
2406 XMLProperty* prop = node->add_property("id");
2407 prop->set_value(v->trackview().route()->id().to_s());
2414 RouteTimeAxisView::remove_underlay (StreamView* v)
2420 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2421 RouteTimeAxisView& other = v->trackview();
2423 if (it != _underlay_streams.end()) {
2424 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2426 if (gm == other._underlay_mirrors.end()) {
2427 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2431 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2433 _underlay_streams.erase(it);
2434 other._underlay_mirrors.erase(gm);
2436 if (underlay_xml_node) {
2437 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2443 RouteTimeAxisView::set_button_names ()
2445 if (_route && _route->solo_safe()) {
2446 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2448 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2450 if (Config->get_solo_control_is_listen_control()) {
2451 switch (Config->get_listen_position()) {
2452 case AfterFaderListen:
2453 solo_button->set_text (_("A"));
2454 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2456 case PreFaderListen:
2457 solo_button->set_text (_("P"));
2458 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2462 solo_button->set_text (_("s"));
2463 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2465 mute_button->set_text (_("m"));
2469 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2471 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2472 if (i != _main_automation_menu_map.end()) {
2476 i = _subplugin_menu_map.find (param);
2477 if (i != _subplugin_menu_map.end()) {
2485 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2487 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2489 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2493 gain_track.reset (new AutomationTimeAxisView (_session,
2494 _route, _route->amp(), c, param,
2499 _route->amp()->describe_parameter(param)));
2502 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2505 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2509 void add_region_to_list (RegionView* rv, RegionList* l)
2511 l->push_back (rv->region());
2515 RouteTimeAxisView::combine_regions ()
2517 /* as of may 2011, we do not offer uncombine for MIDI tracks
2520 if (!is_audio_track()) {
2528 RegionList selected_regions;
2529 boost::shared_ptr<Playlist> playlist = track()->playlist();
2531 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2533 if (selected_regions.size() < 2) {
2537 playlist->clear_changes ();
2538 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2540 _session->add_command (new StatefulDiffCommand (playlist));
2541 /* make the new region be selected */
2543 return _view->find_view (compound_region);
2547 RouteTimeAxisView::uncombine_regions ()
2549 /* as of may 2011, we do not offer uncombine for MIDI tracks
2551 if (!is_audio_track()) {
2559 RegionList selected_regions;
2560 boost::shared_ptr<Playlist> playlist = track()->playlist();
2562 /* have to grab selected regions first because the uncombine is going
2563 * to change that in the middle of the list traverse
2566 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2568 playlist->clear_changes ();
2570 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2571 playlist->uncombine (*i);
2574 _session->add_command (new StatefulDiffCommand (playlist));
2578 RouteTimeAxisView::state_id() const
2580 return string_compose ("rtav %1", _route->id().to_s());
2585 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2587 TimeAxisView::remove_child (c);
2589 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2591 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2592 if (i->second == a) {
2593 _automation_tracks.erase (i);