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, 75, 20)
109 , _ignore_set_layer_display (false)
111 number_label.set_name("route button");
112 number_label.set_alignment(.5, .5);
113 number_label.set_fallthrough_to_parent (true);
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, 0, 1, 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 top_hbox.pack_end(*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, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 if (!_route->is_master()) {
214 controls_table.attach (*solo_button, 2, 3, 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, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
219 name_table.attach (gm.get_gain_slider(), 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 2);
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, 1, 2, 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, 0, 1, 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 (number_label.get_parent()) {
369 name_hbox.remove (number_label);
372 // controls_table.resize ( 2, 4 );
373 name_hbox.pack_start(number_label, false, false, 2);
374 // controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
375 number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
376 number_label.show ();
378 // controls_table.resize ( 2, 3 );
379 number_label.hide ();
384 RouteTimeAxisView::parameter_changed (string const & p)
386 if (p == "track-name-number") {
387 update_track_number_visibility();
392 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
394 if (what_changed.contains (ARDOUR::Properties::name)) {
400 RouteTimeAxisView::take_name_changed (void *src)
408 RouteTimeAxisView::playlist_click ()
410 build_playlist_menu ();
411 conditionally_add_to_selection ();
412 playlist_action_menu->popup (1, gtk_get_current_event_time());
416 RouteTimeAxisView::automation_click ()
418 conditionally_add_to_selection ();
419 build_automation_action_menu (false);
420 automation_action_menu->popup (1, gtk_get_current_event_time());
424 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
426 using namespace Menu_Helpers;
428 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
429 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
432 detach_menu (subplugin_menu);
434 _main_automation_menu_map.clear ();
435 delete automation_action_menu;
436 automation_action_menu = new Menu;
438 MenuList& items = automation_action_menu->items();
440 automation_action_menu->set_name ("ArdourContextMenu");
442 items.push_back (MenuElem (_("Show All Automation"),
443 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
445 items.push_back (MenuElem (_("Show Existing Automation"),
446 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
448 items.push_back (MenuElem (_("Hide All Automation"),
449 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
451 /* Attach the plugin submenu. It may have previously been used elsewhere,
452 so it was detached above
455 if (!subplugin_menu.items().empty()) {
456 items.push_back (SeparatorElem ());
457 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
458 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
463 RouteTimeAxisView::build_display_menu ()
465 using namespace Menu_Helpers;
469 TimeAxisView::build_display_menu ();
471 /* now fill it with our stuff */
473 MenuList& items = display_menu->items();
474 display_menu->set_name ("ArdourContextMenu");
476 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
478 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
480 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
482 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
484 items.push_back (SeparatorElem());
487 detach_menu (*_size_menu);
490 items.push_back (MenuElem (_("Height"), *_size_menu));
492 items.push_back (SeparatorElem());
494 if (!Profile->get_sae()) {
495 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
496 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
497 items.push_back (SeparatorElem());
500 // Hook for derived classes to add type specific stuff
501 append_extra_display_menu_items ();
505 Menu* layers_menu = manage (new Menu);
506 MenuList &layers_items = layers_menu->items();
507 layers_menu->set_name("ArdourContextMenu");
509 RadioMenuItem::Group layers_group;
511 /* Find out how many overlaid/stacked tracks we have in the selection */
515 TrackSelection const & s = _editor.get_selection().tracks;
516 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
517 StreamView* v = (*i)->view ();
522 switch (v->layer_display ()) {
533 /* We're not connecting to signal_toggled() here; in the case where these two items are
534 set to be in the `inconsistent' state, it seems that one or other will end up active
535 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
536 select the active one, no toggled signal is emitted so nothing happens.
539 _ignore_set_layer_display = true;
541 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
542 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
543 i->set_active (overlaid != 0 && stacked == 0);
544 i->set_inconsistent (overlaid != 0 && stacked != 0);
545 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
547 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
548 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
549 i->set_active (overlaid == 0 && stacked != 0);
550 i->set_inconsistent (overlaid != 0 && stacked != 0);
551 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
553 _ignore_set_layer_display = false;
555 items.push_back (MenuElem (_("Layers"), *layers_menu));
557 if (!Profile->get_sae()) {
559 Menu* alignment_menu = manage (new Menu);
560 MenuList& alignment_items = alignment_menu->items();
561 alignment_menu->set_name ("ArdourContextMenu");
563 RadioMenuItem::Group align_group;
565 /* Same verbose hacks as for the layering options above */
571 boost::shared_ptr<Track> first_track;
573 TrackSelection const & s = _editor.get_selection().tracks;
574 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
575 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
576 if (!r || !r->is_track ()) {
581 first_track = r->track();
584 switch (r->track()->alignment_choice()) {
588 switch (r->track()->alignment_style()) {
589 case ExistingMaterial:
597 case UseExistingMaterial:
613 inconsistent = false;
622 if (!inconsistent && first_track) {
624 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
625 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
626 i->set_active (automatic != 0 && existing == 0 && capture == 0);
627 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
629 switch (first_track->alignment_choice()) {
631 switch (first_track->alignment_style()) {
632 case ExistingMaterial:
633 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
636 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
644 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
645 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
646 i->set_active (existing != 0 && capture == 0 && automatic == 0);
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
649 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
650 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
651 i->set_active (existing == 0 && capture != 0 && automatic == 0);
652 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
654 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
660 Menu* mode_menu = manage (new Menu);
661 MenuList& mode_items = mode_menu->items ();
662 mode_menu->set_name ("ArdourContextMenu");
664 RadioMenuItem::Group mode_group;
670 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
671 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
672 if (!r || !r->is_track ()) {
676 switch (r->track()->mode()) {
689 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
690 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
691 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
692 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
693 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
695 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
696 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
697 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
698 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
699 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
701 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
702 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
703 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
704 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
705 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
707 items.push_back (MenuElem (_("Mode"), *mode_menu));
711 items.push_back (SeparatorElem());
713 build_playlist_menu ();
714 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
715 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
718 route_group_menu->detach ();
721 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
722 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
724 r.push_back (rtv->route ());
729 r.push_back (route ());
732 route_group_menu->build (r);
733 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
735 build_automation_action_menu (true);
736 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
738 items.push_back (SeparatorElem());
742 TrackSelection const & s = _editor.get_selection().tracks;
743 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
744 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
749 if (r->route()->active()) {
756 items.push_back (CheckMenuElem (_("Active")));
757 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
758 bool click_sets_active = true;
759 if (active > 0 && inactive == 0) {
760 i->set_active (true);
761 click_sets_active = false;
762 } else if (active > 0 && inactive > 0) {
763 i->set_inconsistent (true);
765 i->set_sensitive(! _session->transport_rolling());
766 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
768 items.push_back (SeparatorElem());
769 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
770 if (!Profile->get_sae()) {
771 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
773 items.push_front (SeparatorElem());
774 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
779 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
781 if (apply_to_selection) {
782 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
785 bool needs_bounce = false;
787 if (!track()->can_use_mode (mode, needs_bounce)) {
793 cerr << "would bounce this one\n";
798 track()->set_mode (mode);
800 rec_enable_button->remove ();
803 case ARDOUR::NonLayered:
805 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
806 rec_enable_button->set_text (string());
808 case ARDOUR::Destructive:
809 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
810 rec_enable_button->set_text (string());
814 rec_enable_button->show_all ();
819 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
821 TimeAxisView::show_timestretch (start, end, layers, layer);
831 /* check that the time selection was made in our route, or our route group.
832 remember that route_group() == 0 implies the route is *not* in a edit group.
835 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
836 /* this doesn't apply to us */
840 /* ignore it if our edit group is not active */
842 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
847 if (timestretch_rect == 0) {
848 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
849 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
850 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
853 timestretch_rect->show ();
854 timestretch_rect->raise_to_top ();
856 double const x1 = start / _editor.get_current_zoom();
857 double const x2 = (end - 1) / _editor.get_current_zoom();
859 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
860 x2, current_height() * (layers - layer) / layers));
864 RouteTimeAxisView::hide_timestretch ()
866 TimeAxisView::hide_timestretch ();
868 if (timestretch_rect) {
869 timestretch_rect->hide ();
874 RouteTimeAxisView::show_selection (TimeSelection& ts)
878 /* ignore it if our edit group is not active or if the selection was started
879 in some other track or route group (remember that route_group() == 0 means
880 that the track is not in an route group).
883 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
884 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
890 TimeAxisView::show_selection (ts);
894 RouteTimeAxisView::set_height (uint32_t h)
897 bool height_changed = (height == 0) || (h != height);
900 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
903 gm.get_level_meter().setup_meters (gmlen, meter_width);
905 TimeAxisView::set_height (h);
908 _view->set_height ((double) current_height());
911 if (height >= preset_height (HeightNormal)) {
915 gm.get_gain_slider().show();
917 if (!_route || _route->is_monitor()) {
922 if (rec_enable_button)
923 rec_enable_button->show();
925 route_group_button.show();
926 automation_button.show();
928 if (is_track() && track()->mode() == ARDOUR::Normal) {
929 playlist_button.show();
936 gm.get_gain_slider().hide();
938 if (!_route || _route->is_monitor()) {
943 if (rec_enable_button)
944 rec_enable_button->show();
946 route_group_button.hide ();
947 automation_button.hide ();
949 if (is_track() && track()->mode() == ARDOUR::Normal) {
950 playlist_button.hide ();
955 if (height_changed && !no_redraw) {
956 /* only emit the signal if the height really changed */
962 RouteTimeAxisView::route_color_changed ()
965 _view->apply_color (color(), StreamView::RegionColor);
968 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
972 RouteTimeAxisView::reset_samples_per_pixel ()
974 set_samples_per_pixel (_editor.get_current_zoom());
978 RouteTimeAxisView::set_samples_per_pixel (double fpp)
983 speed = track()->speed();
987 _view->set_samples_per_pixel (fpp * speed);
990 TimeAxisView::set_samples_per_pixel (fpp * speed);
994 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
996 if (!mitem->get_active()) {
997 /* this is one of the two calls made when these radio menu items change status. this one
998 is for the item that became inactive, and we want to ignore it.
1003 if (apply_to_selection) {
1004 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1007 track()->set_align_choice (choice);
1013 RouteTimeAxisView::rename_current_playlist ()
1015 ArdourPrompter prompter (true);
1018 boost::shared_ptr<Track> tr = track();
1019 if (!tr || tr->destructive()) {
1023 boost::shared_ptr<Playlist> pl = tr->playlist();
1028 prompter.set_title (_("Rename Playlist"));
1029 prompter.set_prompt (_("New name for playlist:"));
1030 prompter.set_initial_text (pl->name());
1031 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1032 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1034 switch (prompter.run ()) {
1035 case Gtk::RESPONSE_ACCEPT:
1036 prompter.get_result (name);
1037 if (name.length()) {
1038 pl->set_name (name);
1048 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1050 std::string ret (basename);
1052 std::string const group_string = "." + route_group()->name() + ".";
1054 // iterate through all playlists
1056 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1057 std::string tmp = (*i)->name();
1059 std::string::size_type idx = tmp.find(group_string);
1060 // find those which belong to this group
1061 if (idx != string::npos) {
1062 tmp = tmp.substr(idx + group_string.length());
1064 // and find the largest current number
1066 if (x > maxnumber) {
1075 snprintf (buf, sizeof(buf), "%d", maxnumber);
1077 ret = this->name() + "." + route_group()->name () + "." + buf;
1083 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1087 boost::shared_ptr<Track> tr = track ();
1088 if (!tr || tr->destructive()) {
1092 boost::shared_ptr<const Playlist> pl = tr->playlist();
1099 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1100 name = resolve_new_group_playlist_name(name, playlists_before_op);
1103 while (_session->playlists->by_name(name)) {
1104 name = Playlist::bump_name (name, *_session);
1107 // TODO: The prompter "new" button should be de-activated if the user
1108 // specifies a playlist name which already exists in the session.
1112 ArdourPrompter prompter (true);
1114 prompter.set_title (_("New Copy 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);
1119 prompter.show_all ();
1121 switch (prompter.run ()) {
1122 case Gtk::RESPONSE_ACCEPT:
1123 prompter.get_result (name);
1131 if (name.length()) {
1132 tr->use_copy_playlist ();
1133 tr->playlist()->set_name (name);
1138 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1142 boost::shared_ptr<Track> tr = track ();
1143 if (!tr || tr->destructive()) {
1147 boost::shared_ptr<const Playlist> pl = tr->playlist();
1154 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1155 name = resolve_new_group_playlist_name(name,playlists_before_op);
1158 while (_session->playlists->by_name(name)) {
1159 name = Playlist::bump_name (name, *_session);
1165 ArdourPrompter prompter (true);
1167 prompter.set_title (_("New Playlist"));
1168 prompter.set_prompt (_("Name for new playlist:"));
1169 prompter.set_initial_text (name);
1170 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1171 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1173 switch (prompter.run ()) {
1174 case Gtk::RESPONSE_ACCEPT:
1175 prompter.get_result (name);
1183 if (name.length()) {
1184 tr->use_new_playlist ();
1185 tr->playlist()->set_name (name);
1190 RouteTimeAxisView::clear_playlist ()
1192 boost::shared_ptr<Track> tr = track ();
1193 if (!tr || tr->destructive()) {
1197 boost::shared_ptr<Playlist> pl = tr->playlist();
1202 _editor.clear_playlist (pl);
1206 RouteTimeAxisView::speed_changed ()
1208 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1212 RouteTimeAxisView::update_diskstream_display ()
1222 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1224 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1226 /* special case: select/deselect all tracks */
1227 if (_editor.get_selection().selected (this)) {
1228 _editor.get_selection().clear_tracks ();
1230 _editor.select_all_tracks ();
1236 switch (ArdourKeyboard::selection_type (ev->state)) {
1237 case Selection::Toggle:
1238 _editor.get_selection().toggle (this);
1241 case Selection::Set:
1242 _editor.get_selection().set (this);
1245 case Selection::Extend:
1246 _editor.extend_selection_to_track (*this);
1249 case Selection::Add:
1250 _editor.get_selection().add (this);
1256 RouteTimeAxisView::set_selected_points (PointSelection& points)
1258 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1259 (*i)->set_selected_points (points);
1264 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1267 _view->set_selected_regionviews (regions);
1271 /** Add the selectable things that we have to a list.
1272 * @param results List to add things to.
1275 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1280 speed = track()->speed();
1283 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1284 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1286 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1287 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1290 /* pick up visible automation tracks */
1292 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1293 if (!(*i)->hidden()) {
1294 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1300 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1303 _view->get_inverted_selectables (sel, results);
1306 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1307 if (!(*i)->hidden()) {
1308 (*i)->get_inverted_selectables (sel, results);
1316 RouteTimeAxisView::route_group () const
1318 return _route->route_group();
1322 RouteTimeAxisView::name() const
1324 return _route->name();
1327 boost::shared_ptr<Playlist>
1328 RouteTimeAxisView::playlist () const
1330 boost::shared_ptr<Track> tr;
1332 if ((tr = track()) != 0) {
1333 return tr->playlist();
1335 return boost::shared_ptr<Playlist> ();
1340 RouteTimeAxisView::name_entry_changed ()
1342 TimeAxisView::name_entry_changed ();
1344 string x = name_entry->get_text ();
1346 if (x == _route->name()) {
1350 strip_whitespace_edges (x);
1352 if (x.length() == 0) {
1353 name_entry->set_text (_route->name());
1357 if (_session->route_name_internal (x)) {
1358 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1360 name_entry->grab_focus ();
1361 } else if (RouteUI::verify_new_route_name (x)) {
1362 _route->set_name (x);
1364 name_entry->grab_focus ();
1368 boost::shared_ptr<Region>
1369 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1371 boost::shared_ptr<Playlist> pl = playlist ();
1374 return pl->find_next_region (pos, point, dir);
1377 return boost::shared_ptr<Region> ();
1381 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1383 boost::shared_ptr<Playlist> pl = playlist ();
1386 return pl->find_next_region_boundary (pos, dir);
1393 RouteTimeAxisView::fade_range (TimeSelection& selection)
1395 boost::shared_ptr<Playlist> what_we_got;
1396 boost::shared_ptr<Track> tr = track ();
1397 boost::shared_ptr<Playlist> playlist;
1400 /* route is a bus, not a track */
1404 playlist = tr->playlist();
1406 TimeSelection time (selection);
1407 float const speed = tr->speed();
1408 if (speed != 1.0f) {
1409 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1410 (*i).start = session_frame_to_track_frame((*i).start, speed);
1411 (*i).end = session_frame_to_track_frame((*i).end, speed);
1415 playlist->clear_changes ();
1416 playlist->clear_owned_changes ();
1418 playlist->fade_range (time);
1420 vector<Command*> cmds;
1421 playlist->rdiff (cmds);
1422 _session->add_commands (cmds);
1423 _session->add_command (new StatefulDiffCommand (playlist));
1428 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1430 boost::shared_ptr<Playlist> what_we_got;
1431 boost::shared_ptr<Track> tr = track ();
1432 boost::shared_ptr<Playlist> playlist;
1435 /* route is a bus, not a track */
1439 playlist = tr->playlist();
1441 TimeSelection time (selection.time);
1442 float const speed = tr->speed();
1443 if (speed != 1.0f) {
1444 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1445 (*i).start = session_frame_to_track_frame((*i).start, speed);
1446 (*i).end = session_frame_to_track_frame((*i).end, speed);
1450 playlist->clear_changes ();
1451 playlist->clear_owned_changes ();
1455 if (playlist->cut (time) != 0) {
1456 if (Config->get_edit_mode() == Ripple)
1457 playlist->ripple(time.start(), -time.length(), NULL);
1458 // no need to exclude any regions from rippling here
1460 vector<Command*> cmds;
1461 playlist->rdiff (cmds);
1462 _session->add_commands (cmds);
1464 _session->add_command (new StatefulDiffCommand (playlist));
1469 if ((what_we_got = playlist->cut (time)) != 0) {
1470 _editor.get_cut_buffer().add (what_we_got);
1471 if (Config->get_edit_mode() == Ripple)
1472 playlist->ripple(time.start(), -time.length(), NULL);
1473 // no need to exclude any regions from rippling here
1475 vector<Command*> cmds;
1476 playlist->rdiff (cmds);
1477 _session->add_commands (cmds);
1479 _session->add_command (new StatefulDiffCommand (playlist));
1483 if ((what_we_got = playlist->copy (time)) != 0) {
1484 _editor.get_cut_buffer().add (what_we_got);
1489 if ((what_we_got = playlist->cut (time)) != 0) {
1490 if (Config->get_edit_mode() == Ripple)
1491 playlist->ripple(time.start(), -time.length(), NULL);
1492 // no need to exclude any regions from rippling here
1494 vector<Command*> cmds;
1495 playlist->rdiff (cmds);
1496 _session->add_commands (cmds);
1497 _session->add_command (new StatefulDiffCommand (playlist));
1498 what_we_got->release ();
1505 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1511 boost::shared_ptr<Playlist> pl = playlist ();
1512 PlaylistSelection::iterator p;
1514 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1516 if (p == selection.playlists.end()) {
1520 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1522 if (track()->speed() != 1.0f) {
1523 pos = session_frame_to_track_frame (pos, track()->speed());
1524 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1527 pl->clear_changes ();
1528 if (Config->get_edit_mode() == Ripple) {
1529 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1530 framecnt_t amount = extent.second - extent.first;
1531 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1533 pl->paste (*p, pos, times);
1535 vector<Command*> cmds;
1537 _session->add_commands (cmds);
1539 _session->add_command (new StatefulDiffCommand (pl));
1545 struct PlaylistSorter {
1546 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1547 return a->sort_id() < b->sort_id();
1552 RouteTimeAxisView::build_playlist_menu ()
1554 using namespace Menu_Helpers;
1560 delete playlist_action_menu;
1561 playlist_action_menu = new Menu;
1562 playlist_action_menu->set_name ("ArdourContextMenu");
1564 MenuList& playlist_items = playlist_action_menu->items();
1565 playlist_action_menu->set_name ("ArdourContextMenu");
1566 playlist_items.clear();
1568 RadioMenuItem::Group playlist_group;
1569 boost::shared_ptr<Track> tr = track ();
1571 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1573 /* sort the playlists */
1575 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1577 /* add the playlists to the menu */
1578 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1579 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1580 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1581 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1583 if (tr->playlist()->id() == (*i)->id()) {
1589 playlist_items.push_back (SeparatorElem());
1590 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1591 playlist_items.push_back (SeparatorElem());
1593 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1594 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1595 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1598 // Use a label which tells the user what is happening
1599 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1600 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1604 playlist_items.push_back (SeparatorElem());
1605 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1606 playlist_items.push_back (SeparatorElem());
1608 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1612 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1614 assert (is_track());
1616 // exit if we were triggered by deactivating the old playlist
1617 if (!item->get_active()) {
1621 boost::shared_ptr<Playlist> pl (wpl.lock());
1627 if (track()->playlist() == pl) {
1628 // exit when use_playlist is called by the creation of the playlist menu
1629 // or the playlist choice is unchanged
1633 track()->use_playlist (pl);
1635 RouteGroup* rg = route_group();
1637 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1638 std::string group_string = "." + rg->name() + ".";
1640 std::string take_name = pl->name();
1641 std::string::size_type idx = take_name.find(group_string);
1643 if (idx == std::string::npos)
1646 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1648 boost::shared_ptr<RouteList> rl (rg->route_list());
1650 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1651 if ((*i) == this->route()) {
1655 std::string playlist_name = (*i)->name()+group_string+take_name;
1657 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1662 if (track->freeze_state() == Track::Frozen) {
1663 /* Don't change playlists of frozen tracks */
1667 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1669 // No playlist for this track for this take yet, make it
1670 track->use_new_playlist();
1671 track->playlist()->set_name(playlist_name);
1673 track->use_playlist(ipl);
1680 RouteTimeAxisView::update_playlist_tip ()
1682 RouteGroup* rg = route_group ();
1683 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1684 string group_string = "." + rg->name() + ".";
1686 string take_name = track()->playlist()->name();
1687 string::size_type idx = take_name.find(group_string);
1689 if (idx != string::npos) {
1690 /* find the bit containing the take number / name */
1691 take_name = take_name.substr (idx + group_string.length());
1693 /* set the playlist button tooltip to the take name */
1694 ARDOUR_UI::instance()->set_tip (
1696 string_compose(_("Take: %1.%2"),
1697 Glib::Markup::escape_text(rg->name()),
1698 Glib::Markup::escape_text(take_name))
1705 /* set the playlist button tooltip to the playlist name */
1706 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1711 RouteTimeAxisView::show_playlist_selector ()
1713 _editor.playlist_selector().show_for (this);
1717 RouteTimeAxisView::map_frozen ()
1723 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1725 switch (track()->freeze_state()) {
1727 playlist_button.set_sensitive (false);
1728 rec_enable_button->set_sensitive (false);
1731 playlist_button.set_sensitive (true);
1732 rec_enable_button->set_sensitive (true);
1738 RouteTimeAxisView::color_handler ()
1740 //case cTimeStretchOutline:
1741 if (timestretch_rect) {
1742 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1744 //case cTimeStretchFill:
1745 if (timestretch_rect) {
1746 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1752 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1753 * Will add track if necessary.
1756 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1758 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1759 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1762 /* it doesn't exist yet, so we don't care about the button state: just add it */
1763 create_automation_child (param, true);
1766 bool yn = menu->get_active();
1767 bool changed = false;
1769 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1771 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1772 will have done that for us.
1775 if (changed && !no_redraw) {
1783 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1785 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1791 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1793 if (menu && !_hidden) {
1794 ignore_toggle = true;
1795 menu->set_active (false);
1796 ignore_toggle = false;
1799 if (_route && !no_redraw) {
1806 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1808 if (apply_to_selection) {
1809 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1813 /* Show our automation */
1815 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1816 i->second->set_marked_for_display (true);
1818 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1821 menu->set_active(true);
1826 /* Show processor automation */
1828 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1829 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1830 if ((*ii)->view == 0) {
1831 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1834 (*ii)->menu_item->set_active (true);
1847 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1849 if (apply_to_selection) {
1850 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1854 /* Show our automation */
1856 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1857 if (i->second->has_automation()) {
1858 i->second->set_marked_for_display (true);
1860 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1862 menu->set_active(true);
1867 /* Show processor automation */
1869 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1870 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1871 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1872 (*ii)->menu_item->set_active (true);
1884 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1886 if (apply_to_selection) {
1887 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1891 /* Hide our automation */
1893 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1894 i->second->set_marked_for_display (false);
1896 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1899 menu->set_active (false);
1903 /* Hide processor automation */
1905 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1906 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1907 (*ii)->menu_item->set_active (false);
1918 RouteTimeAxisView::region_view_added (RegionView* rv)
1920 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1921 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1922 boost::shared_ptr<AutomationTimeAxisView> atv;
1924 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1929 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1930 (*i)->add_ghost(rv);
1934 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1936 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1942 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1944 parent.remove_processor_automation_node (this);
1948 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1951 remove_child (pan->view);
1955 RouteTimeAxisView::ProcessorAutomationNode*
1956 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1958 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1960 if ((*i)->processor == processor) {
1962 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1963 if ((*ii)->what == what) {
1973 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1975 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1978 ProcessorAutomationNode* pan;
1980 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1981 /* session state may never have been saved with new plugin */
1982 error << _("programming error: ")
1983 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1984 processor->name(), what.type(), (int) what.channel(), what.id() )
1994 boost::shared_ptr<AutomationControl> control
1995 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1997 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1998 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1999 _editor, *this, false, parent_canvas,
2000 processor->describe_parameter (what), processor->name()));
2002 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2004 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2007 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2012 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2015 pan->menu_item->set_active (false);
2024 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2026 boost::shared_ptr<Processor> processor (p.lock ());
2028 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2029 /* The Amp processor is a special case and is dealt with separately */
2033 set<Evoral::Parameter> existing;
2035 processor->what_has_data (existing);
2037 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2039 Evoral::Parameter param (*i);
2040 boost::shared_ptr<AutomationLine> al;
2042 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2045 add_processor_automation_curve (processor, param);
2051 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2053 using namespace Menu_Helpers;
2057 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2059 _automation_tracks[param] = track;
2061 /* existing state overrides "show" argument */
2062 string s = track->gui_property ("visible");
2064 show = string_is_affirmative (s);
2067 /* this might or might not change the visibility status, so don't rely on it */
2068 track->set_marked_for_display (show);
2070 if (show && !no_redraw) {
2074 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2075 /* MIDI-related parameters are always in the menu, there's no
2076 reason to rebuild the menu just because we added a automation
2077 lane for one of them. But if we add a non-MIDI automation
2078 lane, then we need to invalidate the display menu.
2080 delete display_menu;
2086 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2088 boost::shared_ptr<Processor> processor (p.lock ());
2090 if (!processor || !processor->display_to_user ()) {
2094 /* we use this override to veto the Amp processor from the plugin menu,
2095 as its automation lane can be accessed using the special "Fader" menu
2099 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2103 using namespace Menu_Helpers;
2104 ProcessorAutomationInfo *rai;
2105 list<ProcessorAutomationInfo*>::iterator x;
2107 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2109 if (automatable.empty()) {
2113 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2114 if ((*x)->processor == processor) {
2119 if (x == processor_automation.end()) {
2121 rai = new ProcessorAutomationInfo (processor);
2122 processor_automation.push_back (rai);
2130 /* any older menu was deleted at the top of processors_changed()
2131 when we cleared the subplugin menu.
2134 rai->menu = manage (new Menu);
2135 MenuList& items = rai->menu->items();
2136 rai->menu->set_name ("ArdourContextMenu");
2140 std::set<Evoral::Parameter> has_visible_automation;
2141 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2143 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2145 ProcessorAutomationNode* pan;
2146 Gtk::CheckMenuItem* mitem;
2148 string name = processor->describe_parameter (*i);
2150 items.push_back (CheckMenuElem (name));
2151 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2153 _subplugin_menu_map[*i] = mitem;
2155 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2156 mitem->set_active(true);
2159 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2163 pan = new ProcessorAutomationNode (*i, mitem, *this);
2165 rai->lines.push_back (pan);
2169 pan->menu_item = mitem;
2173 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2176 /* add the menu for this processor, because the subplugin
2177 menu is always cleared at the top of processors_changed().
2178 this is the result of some poor design in gtkmm and/or
2182 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2187 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2188 RouteTimeAxisView::ProcessorAutomationNode* pan)
2190 bool showit = pan->menu_item->get_active();
2191 bool redraw = false;
2193 if (pan->view == 0 && showit) {
2194 add_processor_automation_curve (rai->processor, pan->what);
2198 if (pan->view && pan->view->set_marked_for_display (showit)) {
2202 if (redraw && !no_redraw) {
2208 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2210 if (c.type == RouteProcessorChange::MeterPointChange) {
2211 /* nothing to do if only the meter point has changed */
2215 using namespace Menu_Helpers;
2217 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2218 (*i)->valid = false;
2221 setup_processor_menu_and_curves ();
2223 bool deleted_processor_automation = false;
2225 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2227 list<ProcessorAutomationInfo*>::iterator tmp;
2235 processor_automation.erase (i);
2236 deleted_processor_automation = true;
2243 if (deleted_processor_automation && !no_redraw) {
2248 boost::shared_ptr<AutomationLine>
2249 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2251 ProcessorAutomationNode* pan;
2253 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2259 return boost::shared_ptr<AutomationLine>();
2263 RouteTimeAxisView::reset_processor_automation_curves ()
2265 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2271 RouteTimeAxisView::can_edit_name () const
2273 /* we do not allow track name changes if it is record enabled
2275 return !_route->record_enabled();
2279 RouteTimeAxisView::blink_rec_display (bool onoff)
2281 RouteUI::blink_rec_display (onoff);
2285 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2287 if (_ignore_set_layer_display) {
2291 if (apply_to_selection) {
2292 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2296 _view->set_layer_display (d);
2299 set_gui_property (X_("layer-display"), enum_2_string (d));
2304 RouteTimeAxisView::layer_display () const
2307 return _view->layer_display ();
2310 /* we don't know, since we don't have a _view, so just return something */
2316 boost::shared_ptr<AutomationTimeAxisView>
2317 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2319 AutomationTracks::iterator i = _automation_tracks.find(param);
2320 if (i != _automation_tracks.end()) {
2323 return boost::shared_ptr<AutomationTimeAxisView>();
2328 RouteTimeAxisView::fast_update ()
2330 gm.get_level_meter().update_meters ();
2334 RouteTimeAxisView::hide_meter ()
2337 gm.get_level_meter().hide_meters ();
2341 RouteTimeAxisView::show_meter ()
2347 RouteTimeAxisView::reset_meter ()
2349 if (Config->get_show_track_meters()) {
2350 int meter_width = 3;
2351 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2354 gm.get_level_meter().setup_meters (height - 9, meter_width);
2361 RouteTimeAxisView::clear_meter ()
2363 gm.get_level_meter().clear_meters ();
2367 RouteTimeAxisView::meter_changed ()
2369 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2371 if (_route && !no_redraw) {
2374 // reset peak when meter point changes
2375 gm.reset_peak_display();
2379 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2382 if (_route && !no_redraw) {
2388 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2390 using namespace Menu_Helpers;
2392 if (!_underlay_streams.empty()) {
2393 MenuList& parent_items = parent_menu->items();
2394 Menu* gs_menu = manage (new Menu);
2395 gs_menu->set_name ("ArdourContextMenu");
2396 MenuList& gs_items = gs_menu->items();
2398 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2400 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2401 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2402 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2408 RouteTimeAxisView::set_underlay_state()
2410 if (!underlay_xml_node) {
2414 XMLNodeList nlist = underlay_xml_node->children();
2415 XMLNodeConstIterator niter;
2416 XMLNode *child_node;
2418 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2419 child_node = *niter;
2421 if (child_node->name() != "Underlay") {
2425 XMLProperty* prop = child_node->property ("id");
2427 PBD::ID id (prop->value());
2429 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2432 add_underlay(v->view(), false);
2441 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2447 RouteTimeAxisView& other = v->trackview();
2449 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2450 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2451 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2455 _underlay_streams.push_back(v);
2456 other._underlay_mirrors.push_back(this);
2458 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2460 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2462 if (!underlay_xml_node) {
2463 underlay_xml_node = xml_node->add_child("Underlays");
2466 XMLNode* node = underlay_xml_node->add_child("Underlay");
2467 XMLProperty* prop = node->add_property("id");
2468 prop->set_value(v->trackview().route()->id().to_s());
2475 RouteTimeAxisView::remove_underlay (StreamView* v)
2481 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2482 RouteTimeAxisView& other = v->trackview();
2484 if (it != _underlay_streams.end()) {
2485 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2487 if (gm == other._underlay_mirrors.end()) {
2488 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2492 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2494 _underlay_streams.erase(it);
2495 other._underlay_mirrors.erase(gm);
2497 if (underlay_xml_node) {
2498 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2504 RouteTimeAxisView::set_button_names ()
2506 if (_route && _route->solo_safe()) {
2507 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2509 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2511 if (Config->get_solo_control_is_listen_control()) {
2512 switch (Config->get_listen_position()) {
2513 case AfterFaderListen:
2514 solo_button->set_text (_("A"));
2515 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2517 case PreFaderListen:
2518 solo_button->set_text (_("P"));
2519 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2523 solo_button->set_text (_("S"));
2524 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2526 mute_button->set_text (_("M"));
2530 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2532 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2533 if (i != _main_automation_menu_map.end()) {
2537 i = _subplugin_menu_map.find (param);
2538 if (i != _subplugin_menu_map.end()) {
2546 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2548 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2550 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2554 gain_track.reset (new AutomationTimeAxisView (_session,
2555 _route, _route->amp(), c, param,
2560 _route->amp()->describe_parameter(param)));
2563 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2566 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2570 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2572 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2574 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2578 mute_track.reset (new AutomationTimeAxisView (_session,
2579 _route, _route, c, param,
2584 _route->describe_parameter(param)));
2587 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2590 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2594 void add_region_to_list (RegionView* rv, RegionList* l)
2596 l->push_back (rv->region());
2600 RouteTimeAxisView::combine_regions ()
2602 /* as of may 2011, we do not offer uncombine for MIDI tracks
2605 if (!is_audio_track()) {
2613 RegionList selected_regions;
2614 boost::shared_ptr<Playlist> playlist = track()->playlist();
2616 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2618 if (selected_regions.size() < 2) {
2622 playlist->clear_changes ();
2623 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2625 _session->add_command (new StatefulDiffCommand (playlist));
2626 /* make the new region be selected */
2628 return _view->find_view (compound_region);
2632 RouteTimeAxisView::uncombine_regions ()
2634 /* as of may 2011, we do not offer uncombine for MIDI tracks
2636 if (!is_audio_track()) {
2644 RegionList selected_regions;
2645 boost::shared_ptr<Playlist> playlist = track()->playlist();
2647 /* have to grab selected regions first because the uncombine is going
2648 * to change that in the middle of the list traverse
2651 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2653 playlist->clear_changes ();
2655 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2656 playlist->uncombine (*i);
2659 _session->add_command (new StatefulDiffCommand (playlist));
2663 RouteTimeAxisView::state_id() const
2665 return string_compose ("rtav %1", _route->id().to_s());
2670 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2672 TimeAxisView::remove_child (c);
2674 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2676 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2677 if (i->second == a) {
2678 _automation_tracks.erase (i);