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_elements((ArdourButton::Element)(ArdourButton::Edge|ArdourButton::Body|ArdourButton::Text|ArdourButton::Inactive));
113 number_label.set_alignment(.5, .5);
114 number_label.set_fallthrough_to_parent (true);
116 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
120 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
122 RouteUI::set_route (rt);
124 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
125 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
126 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
129 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
132 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133 gm.get_level_meter().set_no_show_all();
134 gm.get_level_meter().setup_meters(50, meter_width);
135 gm.update_gain_sensitive ();
137 string str = gui_property ("height");
139 set_height (atoi (str));
141 set_height (preset_height (HeightNormal));
144 if (!_route->is_auditioner()) {
145 if (gui_property ("visible").empty()) {
146 set_gui_property ("visible", true);
149 set_gui_property ("visible", false);
153 update_solo_display ();
155 timestretch_rect = 0;
158 ignore_toggle = false;
160 route_group_button.set_name ("route button");
161 playlist_button.set_name ("route button");
162 automation_button.set_name ("route button");
164 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
165 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
166 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
172 switch (track()->mode()) {
174 case ARDOUR::NonLayered:
175 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
176 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
178 case ARDOUR::Destructive:
179 rec_enable_button->set_text (string());
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
184 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
186 if (is_midi_track()) {
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
188 gm.set_fader_name ("MidiTrackFader");
190 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
191 gm.set_fader_name ("AudioTrackFader");
194 rec_enable_button->set_sensitive (_session->writable());
196 /* set playlist button tip to the current playlist, and make it update when it changes */
197 update_playlist_tip ();
198 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
201 gm.set_fader_name ("AudioBusFader");
204 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
205 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
206 top_hbox.pack_end(*mtrbox, false, false, 4);
209 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
210 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
211 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
212 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
214 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
216 if (!_route->is_master()) {
217 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
220 if (!ARDOUR::Profile->get_trx()) {
221 controls_table.attach (route_group_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
222 name_table.attach (gm.get_gain_slider(), 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 2);
225 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
226 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
227 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
229 if (is_midi_track()) {
230 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
232 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
235 update_track_number_visibility();
238 if (!ARDOUR::Profile->get_trx()) {
239 controls_table.attach (automation_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
242 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
243 controls_table.attach (playlist_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
248 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
249 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
253 str = gui_property ("layer-display");
255 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
258 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
259 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
261 /* pick up the correct freeze state */
266 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
267 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
269 PropertyList* plist = new PropertyList();
271 plist->add (ARDOUR::Properties::mute, true);
272 plist->add (ARDOUR::Properties::solo, true);
274 route_group_menu = new RouteGroupMenu (_session, plist);
276 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
278 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
281 RouteTimeAxisView::~RouteTimeAxisView ()
283 CatchDeletion (this);
285 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
289 delete playlist_action_menu;
290 playlist_action_menu = 0;
295 _automation_tracks.clear ();
297 delete route_group_menu;
301 RouteTimeAxisView::post_construct ()
303 /* map current state of the route */
305 update_diskstream_display ();
306 setup_processor_menu_and_curves ();
307 reset_processor_automation_curves ();
310 /** Set up the processor menu for the current set of processors, and
311 * display automation curves for any parameters which have data.
314 RouteTimeAxisView::setup_processor_menu_and_curves ()
316 _subplugin_menu_map.clear ();
317 subplugin_menu.items().clear ();
318 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
319 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
323 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
325 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
326 if (_route->route_group()) {
327 _route->route_group()->remove (_route);
333 r.push_back (route ());
335 route_group_menu->build (r);
336 route_group_menu->menu()->popup (ev->button, ev->time);
342 RouteTimeAxisView::playlist_changed ()
348 RouteTimeAxisView::label_view ()
350 string x = _route->name ();
351 if (x != name_label.get_text ()) {
352 name_label.set_text (x);
354 const int64_t track_number = _route->track_number ();
355 if (track_number == 0) {
356 number_label.set_text ("");
358 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
363 RouteTimeAxisView::update_track_number_visibility ()
365 bool show_label = _session->config.get_track_name_number();
367 if (_route && _route->is_master()) {
371 if (number_label.get_parent()) {
372 name_hbox.remove (number_label);
375 // controls_table.resize ( 2, 4 );
376 name_hbox.pack_start(number_label, false, false, 2);
377 // controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
378 const int tnw = std::max(2u, _session->track_number_decimals()) * 8; // TODO 8 = max_width_of_digit_0_to_9()
379 number_label.set_size_request(3 + tnw, -1);
380 number_label.show ();
382 // controls_table.resize ( 2, 3 );
383 number_label.hide ();
388 RouteTimeAxisView::parameter_changed (string const & p)
390 if (p == "track-name-number") {
391 update_track_number_visibility();
396 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
398 if (what_changed.contains (ARDOUR::Properties::name)) {
404 RouteTimeAxisView::take_name_changed (void *src)
412 RouteTimeAxisView::playlist_click ()
414 build_playlist_menu ();
415 conditionally_add_to_selection ();
416 playlist_action_menu->popup (1, gtk_get_current_event_time());
420 RouteTimeAxisView::automation_click ()
422 conditionally_add_to_selection ();
423 build_automation_action_menu (false);
424 automation_action_menu->popup (1, gtk_get_current_event_time());
428 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
430 using namespace Menu_Helpers;
432 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
433 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
436 detach_menu (subplugin_menu);
438 _main_automation_menu_map.clear ();
439 delete automation_action_menu;
440 automation_action_menu = new Menu;
442 MenuList& items = automation_action_menu->items();
444 automation_action_menu->set_name ("ArdourContextMenu");
446 items.push_back (MenuElem (_("Show All Automation"),
447 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
449 items.push_back (MenuElem (_("Show Existing Automation"),
450 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
452 items.push_back (MenuElem (_("Hide All Automation"),
453 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
455 /* Attach the plugin submenu. It may have previously been used elsewhere,
456 so it was detached above
459 if (!subplugin_menu.items().empty()) {
460 items.push_back (SeparatorElem ());
461 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
462 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
467 RouteTimeAxisView::build_display_menu ()
469 using namespace Menu_Helpers;
473 TimeAxisView::build_display_menu ();
475 /* now fill it with our stuff */
477 MenuList& items = display_menu->items();
478 display_menu->set_name ("ArdourContextMenu");
480 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
482 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
484 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
486 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
488 items.push_back (SeparatorElem());
491 detach_menu (*_size_menu);
494 items.push_back (MenuElem (_("Height"), *_size_menu));
496 items.push_back (SeparatorElem());
498 if (!Profile->get_sae()) {
499 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
500 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
501 items.push_back (SeparatorElem());
504 // Hook for derived classes to add type specific stuff
505 append_extra_display_menu_items ();
509 Menu* layers_menu = manage (new Menu);
510 MenuList &layers_items = layers_menu->items();
511 layers_menu->set_name("ArdourContextMenu");
513 RadioMenuItem::Group layers_group;
515 /* Find out how many overlaid/stacked tracks we have in the selection */
519 TrackSelection const & s = _editor.get_selection().tracks;
520 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
521 StreamView* v = (*i)->view ();
526 switch (v->layer_display ()) {
537 /* We're not connecting to signal_toggled() here; in the case where these two items are
538 set to be in the `inconsistent' state, it seems that one or other will end up active
539 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
540 select the active one, no toggled signal is emitted so nothing happens.
543 _ignore_set_layer_display = true;
545 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
546 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
547 i->set_active (overlaid != 0 && stacked == 0);
548 i->set_inconsistent (overlaid != 0 && stacked != 0);
549 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
551 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
552 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
553 i->set_active (overlaid == 0 && stacked != 0);
554 i->set_inconsistent (overlaid != 0 && stacked != 0);
555 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
557 _ignore_set_layer_display = false;
559 items.push_back (MenuElem (_("Layers"), *layers_menu));
561 if (!Profile->get_sae()) {
563 Menu* alignment_menu = manage (new Menu);
564 MenuList& alignment_items = alignment_menu->items();
565 alignment_menu->set_name ("ArdourContextMenu");
567 RadioMenuItem::Group align_group;
569 /* Same verbose hacks as for the layering options above */
575 boost::shared_ptr<Track> first_track;
577 TrackSelection const & s = _editor.get_selection().tracks;
578 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
579 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
580 if (!r || !r->is_track ()) {
585 first_track = r->track();
588 switch (r->track()->alignment_choice()) {
592 switch (r->track()->alignment_style()) {
593 case ExistingMaterial:
601 case UseExistingMaterial:
617 inconsistent = false;
626 if (!inconsistent && first_track) {
628 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
629 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
630 i->set_active (automatic != 0 && existing == 0 && capture == 0);
631 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
633 switch (first_track->alignment_choice()) {
635 switch (first_track->alignment_style()) {
636 case ExistingMaterial:
637 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
640 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
648 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
649 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
650 i->set_active (existing != 0 && capture == 0 && automatic == 0);
651 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
653 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
654 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
655 i->set_active (existing == 0 && capture != 0 && automatic == 0);
656 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
658 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
664 Menu* mode_menu = manage (new Menu);
665 MenuList& mode_items = mode_menu->items ();
666 mode_menu->set_name ("ArdourContextMenu");
668 RadioMenuItem::Group mode_group;
674 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
675 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
676 if (!r || !r->is_track ()) {
680 switch (r->track()->mode()) {
693 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
694 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
695 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
696 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
697 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
699 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
700 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
701 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
702 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
703 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
705 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
706 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
707 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
708 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
709 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
711 items.push_back (MenuElem (_("Mode"), *mode_menu));
715 items.push_back (SeparatorElem());
717 build_playlist_menu ();
718 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
719 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
722 route_group_menu->detach ();
725 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
726 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
728 r.push_back (rtv->route ());
733 r.push_back (route ());
736 route_group_menu->build (r);
737 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
739 build_automation_action_menu (true);
740 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
742 items.push_back (SeparatorElem());
746 TrackSelection const & s = _editor.get_selection().tracks;
747 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
748 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
753 if (r->route()->active()) {
760 items.push_back (CheckMenuElem (_("Active")));
761 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
762 bool click_sets_active = true;
763 if (active > 0 && inactive == 0) {
764 i->set_active (true);
765 click_sets_active = false;
766 } else if (active > 0 && inactive > 0) {
767 i->set_inconsistent (true);
769 i->set_sensitive(! _session->transport_rolling());
770 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
772 items.push_back (SeparatorElem());
773 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
774 if (!Profile->get_sae()) {
775 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
777 items.push_front (SeparatorElem());
778 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
783 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
785 if (apply_to_selection) {
786 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
789 bool needs_bounce = false;
791 if (!track()->can_use_mode (mode, needs_bounce)) {
797 cerr << "would bounce this one\n";
802 track()->set_mode (mode);
804 rec_enable_button->remove ();
807 case ARDOUR::NonLayered:
809 rec_enable_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
810 rec_enable_button->set_markup ("<span color=\"#f46f6f\">\u25CF</span>");
812 case ARDOUR::Destructive:
813 rec_enable_button->set_text (string());
814 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
818 rec_enable_button->show_all ();
823 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
825 TimeAxisView::show_timestretch (start, end, layers, layer);
835 /* check that the time selection was made in our route, or our route group.
836 remember that route_group() == 0 implies the route is *not* in a edit group.
839 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
840 /* this doesn't apply to us */
844 /* ignore it if our edit group is not active */
846 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
851 if (timestretch_rect == 0) {
852 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
853 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
854 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
857 timestretch_rect->show ();
858 timestretch_rect->raise_to_top ();
860 double const x1 = start / _editor.get_current_zoom();
861 double const x2 = (end - 1) / _editor.get_current_zoom();
863 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
864 x2, current_height() * (layers - layer) / layers));
868 RouteTimeAxisView::hide_timestretch ()
870 TimeAxisView::hide_timestretch ();
872 if (timestretch_rect) {
873 timestretch_rect->hide ();
878 RouteTimeAxisView::show_selection (TimeSelection& ts)
882 /* ignore it if our edit group is not active or if the selection was started
883 in some other track or route group (remember that route_group() == 0 means
884 that the track is not in an route group).
887 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
888 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
894 TimeAxisView::show_selection (ts);
898 RouteTimeAxisView::set_height (uint32_t h)
901 bool height_changed = (height == 0) || (h != height);
904 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
907 gm.get_level_meter().setup_meters (gmlen, meter_width);
909 TimeAxisView::set_height (h);
912 _view->set_height ((double) current_height());
915 if (height >= preset_height (HeightNormal)) {
919 gm.get_gain_slider().show();
921 if (!_route || _route->is_monitor()) {
926 if (rec_enable_button)
927 rec_enable_button->show();
929 route_group_button.show();
930 automation_button.show();
932 if (is_track() && track()->mode() == ARDOUR::Normal) {
933 playlist_button.show();
940 gm.get_gain_slider().hide();
942 if (!_route || _route->is_monitor()) {
947 if (rec_enable_button)
948 rec_enable_button->show();
950 route_group_button.hide ();
951 automation_button.hide ();
953 if (is_track() && track()->mode() == ARDOUR::Normal) {
954 playlist_button.hide ();
959 if (height_changed && !no_redraw) {
960 /* only emit the signal if the height really changed */
966 RouteTimeAxisView::route_color_changed ()
969 _view->apply_color (color(), StreamView::RegionColor);
972 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
976 RouteTimeAxisView::reset_samples_per_pixel ()
978 set_samples_per_pixel (_editor.get_current_zoom());
982 RouteTimeAxisView::set_samples_per_pixel (double fpp)
987 speed = track()->speed();
991 _view->set_samples_per_pixel (fpp * speed);
994 TimeAxisView::set_samples_per_pixel (fpp * speed);
998 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
1000 if (!mitem->get_active()) {
1001 /* this is one of the two calls made when these radio menu items change status. this one
1002 is for the item that became inactive, and we want to ignore it.
1007 if (apply_to_selection) {
1008 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1011 track()->set_align_choice (choice);
1017 RouteTimeAxisView::rename_current_playlist ()
1019 ArdourPrompter prompter (true);
1022 boost::shared_ptr<Track> tr = track();
1023 if (!tr || tr->destructive()) {
1027 boost::shared_ptr<Playlist> pl = tr->playlist();
1032 prompter.set_title (_("Rename Playlist"));
1033 prompter.set_prompt (_("New name for playlist:"));
1034 prompter.set_initial_text (pl->name());
1035 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1036 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1038 switch (prompter.run ()) {
1039 case Gtk::RESPONSE_ACCEPT:
1040 prompter.get_result (name);
1041 if (name.length()) {
1042 pl->set_name (name);
1052 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1054 std::string ret (basename);
1056 std::string const group_string = "." + route_group()->name() + ".";
1058 // iterate through all playlists
1060 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1061 std::string tmp = (*i)->name();
1063 std::string::size_type idx = tmp.find(group_string);
1064 // find those which belong to this group
1065 if (idx != string::npos) {
1066 tmp = tmp.substr(idx + group_string.length());
1068 // and find the largest current number
1070 if (x > maxnumber) {
1079 snprintf (buf, sizeof(buf), "%d", maxnumber);
1081 ret = this->name() + "." + route_group()->name () + "." + buf;
1087 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1091 boost::shared_ptr<Track> tr = track ();
1092 if (!tr || tr->destructive()) {
1096 boost::shared_ptr<const Playlist> pl = tr->playlist();
1103 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1104 name = resolve_new_group_playlist_name(name, playlists_before_op);
1107 while (_session->playlists->by_name(name)) {
1108 name = Playlist::bump_name (name, *_session);
1111 // TODO: The prompter "new" button should be de-activated if the user
1112 // specifies a playlist name which already exists in the session.
1116 ArdourPrompter prompter (true);
1118 prompter.set_title (_("New Copy Playlist"));
1119 prompter.set_prompt (_("Name for new playlist:"));
1120 prompter.set_initial_text (name);
1121 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1122 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1123 prompter.show_all ();
1125 switch (prompter.run ()) {
1126 case Gtk::RESPONSE_ACCEPT:
1127 prompter.get_result (name);
1135 if (name.length()) {
1136 tr->use_copy_playlist ();
1137 tr->playlist()->set_name (name);
1142 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1146 boost::shared_ptr<Track> tr = track ();
1147 if (!tr || tr->destructive()) {
1151 boost::shared_ptr<const Playlist> pl = tr->playlist();
1158 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1159 name = resolve_new_group_playlist_name(name,playlists_before_op);
1162 while (_session->playlists->by_name(name)) {
1163 name = Playlist::bump_name (name, *_session);
1169 ArdourPrompter prompter (true);
1171 prompter.set_title (_("New Playlist"));
1172 prompter.set_prompt (_("Name for new playlist:"));
1173 prompter.set_initial_text (name);
1174 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1175 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1177 switch (prompter.run ()) {
1178 case Gtk::RESPONSE_ACCEPT:
1179 prompter.get_result (name);
1187 if (name.length()) {
1188 tr->use_new_playlist ();
1189 tr->playlist()->set_name (name);
1194 RouteTimeAxisView::clear_playlist ()
1196 boost::shared_ptr<Track> tr = track ();
1197 if (!tr || tr->destructive()) {
1201 boost::shared_ptr<Playlist> pl = tr->playlist();
1206 _editor.clear_playlist (pl);
1210 RouteTimeAxisView::speed_changed ()
1212 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1216 RouteTimeAxisView::update_diskstream_display ()
1226 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1228 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1230 /* special case: select/deselect all tracks */
1231 if (_editor.get_selection().selected (this)) {
1232 _editor.get_selection().clear_tracks ();
1234 _editor.select_all_tracks ();
1240 switch (ArdourKeyboard::selection_type (ev->state)) {
1241 case Selection::Toggle:
1242 _editor.get_selection().toggle (this);
1245 case Selection::Set:
1246 _editor.get_selection().set (this);
1249 case Selection::Extend:
1250 _editor.extend_selection_to_track (*this);
1253 case Selection::Add:
1254 _editor.get_selection().add (this);
1260 RouteTimeAxisView::set_selected_points (PointSelection& points)
1262 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1263 (*i)->set_selected_points (points);
1268 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1271 _view->set_selected_regionviews (regions);
1275 /** Add the selectable things that we have to a list.
1276 * @param results List to add things to.
1279 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1284 speed = track()->speed();
1287 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1288 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1290 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1291 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1294 /* pick up visible automation tracks */
1296 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1297 if (!(*i)->hidden()) {
1298 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1304 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1307 _view->get_inverted_selectables (sel, results);
1310 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1311 if (!(*i)->hidden()) {
1312 (*i)->get_inverted_selectables (sel, results);
1320 RouteTimeAxisView::route_group () const
1322 return _route->route_group();
1326 RouteTimeAxisView::name() const
1328 return _route->name();
1331 boost::shared_ptr<Playlist>
1332 RouteTimeAxisView::playlist () const
1334 boost::shared_ptr<Track> tr;
1336 if ((tr = track()) != 0) {
1337 return tr->playlist();
1339 return boost::shared_ptr<Playlist> ();
1344 RouteTimeAxisView::name_entry_changed ()
1346 TimeAxisView::name_entry_changed ();
1348 string x = name_entry->get_text ();
1350 if (x == _route->name()) {
1354 strip_whitespace_edges (x);
1356 if (x.length() == 0) {
1357 name_entry->set_text (_route->name());
1361 if (_session->route_name_internal (x)) {
1362 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1364 name_entry->grab_focus ();
1365 } else if (RouteUI::verify_new_route_name (x)) {
1366 _route->set_name (x);
1368 name_entry->grab_focus ();
1372 boost::shared_ptr<Region>
1373 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1375 boost::shared_ptr<Playlist> pl = playlist ();
1378 return pl->find_next_region (pos, point, dir);
1381 return boost::shared_ptr<Region> ();
1385 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1387 boost::shared_ptr<Playlist> pl = playlist ();
1390 return pl->find_next_region_boundary (pos, dir);
1397 RouteTimeAxisView::fade_range (TimeSelection& selection)
1399 boost::shared_ptr<Playlist> what_we_got;
1400 boost::shared_ptr<Track> tr = track ();
1401 boost::shared_ptr<Playlist> playlist;
1404 /* route is a bus, not a track */
1408 playlist = tr->playlist();
1410 TimeSelection time (selection);
1411 float const speed = tr->speed();
1412 if (speed != 1.0f) {
1413 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1414 (*i).start = session_frame_to_track_frame((*i).start, speed);
1415 (*i).end = session_frame_to_track_frame((*i).end, speed);
1419 playlist->clear_changes ();
1420 playlist->clear_owned_changes ();
1422 playlist->fade_range (time);
1424 vector<Command*> cmds;
1425 playlist->rdiff (cmds);
1426 _session->add_commands (cmds);
1427 _session->add_command (new StatefulDiffCommand (playlist));
1432 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1434 boost::shared_ptr<Playlist> what_we_got;
1435 boost::shared_ptr<Track> tr = track ();
1436 boost::shared_ptr<Playlist> playlist;
1439 /* route is a bus, not a track */
1443 playlist = tr->playlist();
1445 TimeSelection time (selection.time);
1446 float const speed = tr->speed();
1447 if (speed != 1.0f) {
1448 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1449 (*i).start = session_frame_to_track_frame((*i).start, speed);
1450 (*i).end = session_frame_to_track_frame((*i).end, speed);
1454 playlist->clear_changes ();
1455 playlist->clear_owned_changes ();
1459 if (playlist->cut (time) != 0) {
1460 if (Config->get_edit_mode() == Ripple)
1461 playlist->ripple(time.start(), -time.length(), NULL);
1462 // no need to exclude any regions from rippling here
1464 vector<Command*> cmds;
1465 playlist->rdiff (cmds);
1466 _session->add_commands (cmds);
1468 _session->add_command (new StatefulDiffCommand (playlist));
1473 if ((what_we_got = playlist->cut (time)) != 0) {
1474 _editor.get_cut_buffer().add (what_we_got);
1475 if (Config->get_edit_mode() == Ripple)
1476 playlist->ripple(time.start(), -time.length(), NULL);
1477 // no need to exclude any regions from rippling here
1479 vector<Command*> cmds;
1480 playlist->rdiff (cmds);
1481 _session->add_commands (cmds);
1483 _session->add_command (new StatefulDiffCommand (playlist));
1487 if ((what_we_got = playlist->copy (time)) != 0) {
1488 _editor.get_cut_buffer().add (what_we_got);
1493 if ((what_we_got = playlist->cut (time)) != 0) {
1494 if (Config->get_edit_mode() == Ripple)
1495 playlist->ripple(time.start(), -time.length(), NULL);
1496 // no need to exclude any regions from rippling here
1498 vector<Command*> cmds;
1499 playlist->rdiff (cmds);
1500 _session->add_commands (cmds);
1501 _session->add_command (new StatefulDiffCommand (playlist));
1502 what_we_got->release ();
1509 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1515 boost::shared_ptr<Playlist> pl = playlist ();
1516 PlaylistSelection::iterator p;
1518 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1520 if (p == selection.playlists.end()) {
1524 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1526 if (track()->speed() != 1.0f) {
1527 pos = session_frame_to_track_frame (pos, track()->speed());
1528 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1531 pl->clear_changes ();
1532 if (Config->get_edit_mode() == Ripple) {
1533 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1534 framecnt_t amount = extent.second - extent.first;
1535 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1537 pl->paste (*p, pos, times);
1539 vector<Command*> cmds;
1541 _session->add_commands (cmds);
1543 _session->add_command (new StatefulDiffCommand (pl));
1549 struct PlaylistSorter {
1550 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1551 return a->sort_id() < b->sort_id();
1556 RouteTimeAxisView::build_playlist_menu ()
1558 using namespace Menu_Helpers;
1564 delete playlist_action_menu;
1565 playlist_action_menu = new Menu;
1566 playlist_action_menu->set_name ("ArdourContextMenu");
1568 MenuList& playlist_items = playlist_action_menu->items();
1569 playlist_action_menu->set_name ("ArdourContextMenu");
1570 playlist_items.clear();
1572 RadioMenuItem::Group playlist_group;
1573 boost::shared_ptr<Track> tr = track ();
1575 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1577 /* sort the playlists */
1579 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1581 /* add the playlists to the menu */
1582 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1583 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1584 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1585 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1587 if (tr->playlist()->id() == (*i)->id()) {
1593 playlist_items.push_back (SeparatorElem());
1594 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1595 playlist_items.push_back (SeparatorElem());
1597 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1598 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1599 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1602 // Use a label which tells the user what is happening
1603 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1604 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1608 playlist_items.push_back (SeparatorElem());
1609 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1610 playlist_items.push_back (SeparatorElem());
1612 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1616 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1618 assert (is_track());
1620 // exit if we were triggered by deactivating the old playlist
1621 if (!item->get_active()) {
1625 boost::shared_ptr<Playlist> pl (wpl.lock());
1631 if (track()->playlist() == pl) {
1632 // exit when use_playlist is called by the creation of the playlist menu
1633 // or the playlist choice is unchanged
1637 track()->use_playlist (pl);
1639 RouteGroup* rg = route_group();
1641 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1642 std::string group_string = "." + rg->name() + ".";
1644 std::string take_name = pl->name();
1645 std::string::size_type idx = take_name.find(group_string);
1647 if (idx == std::string::npos)
1650 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1652 boost::shared_ptr<RouteList> rl (rg->route_list());
1654 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1655 if ((*i) == this->route()) {
1659 std::string playlist_name = (*i)->name()+group_string+take_name;
1661 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1666 if (track->freeze_state() == Track::Frozen) {
1667 /* Don't change playlists of frozen tracks */
1671 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1673 // No playlist for this track for this take yet, make it
1674 track->use_new_playlist();
1675 track->playlist()->set_name(playlist_name);
1677 track->use_playlist(ipl);
1684 RouteTimeAxisView::update_playlist_tip ()
1686 RouteGroup* rg = route_group ();
1687 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1688 string group_string = "." + rg->name() + ".";
1690 string take_name = track()->playlist()->name();
1691 string::size_type idx = take_name.find(group_string);
1693 if (idx != string::npos) {
1694 /* find the bit containing the take number / name */
1695 take_name = take_name.substr (idx + group_string.length());
1697 /* set the playlist button tooltip to the take name */
1698 ARDOUR_UI::instance()->set_tip (
1700 string_compose(_("Take: %1.%2"),
1701 Glib::Markup::escape_text(rg->name()),
1702 Glib::Markup::escape_text(take_name))
1709 /* set the playlist button tooltip to the playlist name */
1710 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1715 RouteTimeAxisView::show_playlist_selector ()
1717 _editor.playlist_selector().show_for (this);
1721 RouteTimeAxisView::map_frozen ()
1727 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1729 switch (track()->freeze_state()) {
1731 playlist_button.set_sensitive (false);
1732 rec_enable_button->set_sensitive (false);
1735 playlist_button.set_sensitive (true);
1736 rec_enable_button->set_sensitive (true);
1742 RouteTimeAxisView::color_handler ()
1744 //case cTimeStretchOutline:
1745 if (timestretch_rect) {
1746 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1748 //case cTimeStretchFill:
1749 if (timestretch_rect) {
1750 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1756 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1757 * Will add track if necessary.
1760 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1762 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1763 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1766 /* it doesn't exist yet, so we don't care about the button state: just add it */
1767 create_automation_child (param, true);
1770 bool yn = menu->get_active();
1771 bool changed = false;
1773 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1775 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1776 will have done that for us.
1779 if (changed && !no_redraw) {
1787 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1789 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1795 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1797 if (menu && !_hidden) {
1798 ignore_toggle = true;
1799 menu->set_active (false);
1800 ignore_toggle = false;
1803 if (_route && !no_redraw) {
1810 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1812 if (apply_to_selection) {
1813 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1817 /* Show our automation */
1819 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1820 i->second->set_marked_for_display (true);
1822 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1825 menu->set_active(true);
1830 /* Show processor automation */
1832 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1833 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1834 if ((*ii)->view == 0) {
1835 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1838 (*ii)->menu_item->set_active (true);
1851 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1853 if (apply_to_selection) {
1854 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1858 /* Show our automation */
1860 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1861 if (i->second->has_automation()) {
1862 i->second->set_marked_for_display (true);
1864 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1866 menu->set_active(true);
1871 /* Show processor automation */
1873 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1874 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1875 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1876 (*ii)->menu_item->set_active (true);
1888 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1890 if (apply_to_selection) {
1891 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1895 /* Hide our automation */
1897 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1898 i->second->set_marked_for_display (false);
1900 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1903 menu->set_active (false);
1907 /* Hide processor automation */
1909 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1910 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1911 (*ii)->menu_item->set_active (false);
1922 RouteTimeAxisView::region_view_added (RegionView* rv)
1924 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1925 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1926 boost::shared_ptr<AutomationTimeAxisView> atv;
1928 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1933 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1934 (*i)->add_ghost(rv);
1938 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1940 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1946 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1948 parent.remove_processor_automation_node (this);
1952 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1955 remove_child (pan->view);
1959 RouteTimeAxisView::ProcessorAutomationNode*
1960 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1962 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1964 if ((*i)->processor == processor) {
1966 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1967 if ((*ii)->what == what) {
1977 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1979 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1982 ProcessorAutomationNode* pan;
1984 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1985 /* session state may never have been saved with new plugin */
1986 error << _("programming error: ")
1987 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1988 processor->name(), what.type(), (int) what.channel(), what.id() )
1998 boost::shared_ptr<AutomationControl> control
1999 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
2001 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
2002 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
2003 _editor, *this, false, parent_canvas,
2004 processor->describe_parameter (what), processor->name()));
2006 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2008 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2011 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2016 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2019 pan->menu_item->set_active (false);
2028 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2030 boost::shared_ptr<Processor> processor (p.lock ());
2032 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2033 /* The Amp processor is a special case and is dealt with separately */
2037 set<Evoral::Parameter> existing;
2039 processor->what_has_data (existing);
2041 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2043 Evoral::Parameter param (*i);
2044 boost::shared_ptr<AutomationLine> al;
2046 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2049 add_processor_automation_curve (processor, param);
2055 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2057 using namespace Menu_Helpers;
2061 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2063 _automation_tracks[param] = track;
2065 /* existing state overrides "show" argument */
2066 string s = track->gui_property ("visible");
2068 show = string_is_affirmative (s);
2071 /* this might or might not change the visibility status, so don't rely on it */
2072 track->set_marked_for_display (show);
2074 if (show && !no_redraw) {
2078 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2079 /* MIDI-related parameters are always in the menu, there's no
2080 reason to rebuild the menu just because we added a automation
2081 lane for one of them. But if we add a non-MIDI automation
2082 lane, then we need to invalidate the display menu.
2084 delete display_menu;
2090 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2092 boost::shared_ptr<Processor> processor (p.lock ());
2094 if (!processor || !processor->display_to_user ()) {
2098 /* we use this override to veto the Amp processor from the plugin menu,
2099 as its automation lane can be accessed using the special "Fader" menu
2103 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2107 using namespace Menu_Helpers;
2108 ProcessorAutomationInfo *rai;
2109 list<ProcessorAutomationInfo*>::iterator x;
2111 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2113 if (automatable.empty()) {
2117 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2118 if ((*x)->processor == processor) {
2123 if (x == processor_automation.end()) {
2125 rai = new ProcessorAutomationInfo (processor);
2126 processor_automation.push_back (rai);
2134 /* any older menu was deleted at the top of processors_changed()
2135 when we cleared the subplugin menu.
2138 rai->menu = manage (new Menu);
2139 MenuList& items = rai->menu->items();
2140 rai->menu->set_name ("ArdourContextMenu");
2144 std::set<Evoral::Parameter> has_visible_automation;
2145 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2147 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2149 ProcessorAutomationNode* pan;
2150 Gtk::CheckMenuItem* mitem;
2152 string name = processor->describe_parameter (*i);
2154 items.push_back (CheckMenuElem (name));
2155 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2157 _subplugin_menu_map[*i] = mitem;
2159 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2160 mitem->set_active(true);
2163 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2167 pan = new ProcessorAutomationNode (*i, mitem, *this);
2169 rai->lines.push_back (pan);
2173 pan->menu_item = mitem;
2177 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2180 /* add the menu for this processor, because the subplugin
2181 menu is always cleared at the top of processors_changed().
2182 this is the result of some poor design in gtkmm and/or
2186 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2191 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2192 RouteTimeAxisView::ProcessorAutomationNode* pan)
2194 bool showit = pan->menu_item->get_active();
2195 bool redraw = false;
2197 if (pan->view == 0 && showit) {
2198 add_processor_automation_curve (rai->processor, pan->what);
2202 if (pan->view && pan->view->set_marked_for_display (showit)) {
2206 if (redraw && !no_redraw) {
2212 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2214 if (c.type == RouteProcessorChange::MeterPointChange) {
2215 /* nothing to do if only the meter point has changed */
2219 using namespace Menu_Helpers;
2221 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2222 (*i)->valid = false;
2225 setup_processor_menu_and_curves ();
2227 bool deleted_processor_automation = false;
2229 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2231 list<ProcessorAutomationInfo*>::iterator tmp;
2239 processor_automation.erase (i);
2240 deleted_processor_automation = true;
2247 if (deleted_processor_automation && !no_redraw) {
2252 boost::shared_ptr<AutomationLine>
2253 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2255 ProcessorAutomationNode* pan;
2257 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2263 return boost::shared_ptr<AutomationLine>();
2267 RouteTimeAxisView::reset_processor_automation_curves ()
2269 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2275 RouteTimeAxisView::can_edit_name () const
2277 /* we do not allow track name changes if it is record enabled
2279 return !_route->record_enabled();
2283 RouteTimeAxisView::blink_rec_display (bool onoff)
2285 RouteUI::blink_rec_display (onoff);
2289 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2291 if (_ignore_set_layer_display) {
2295 if (apply_to_selection) {
2296 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2300 _view->set_layer_display (d);
2303 set_gui_property (X_("layer-display"), enum_2_string (d));
2308 RouteTimeAxisView::layer_display () const
2311 return _view->layer_display ();
2314 /* we don't know, since we don't have a _view, so just return something */
2320 boost::shared_ptr<AutomationTimeAxisView>
2321 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2323 AutomationTracks::iterator i = _automation_tracks.find(param);
2324 if (i != _automation_tracks.end()) {
2327 return boost::shared_ptr<AutomationTimeAxisView>();
2332 RouteTimeAxisView::fast_update ()
2334 gm.get_level_meter().update_meters ();
2338 RouteTimeAxisView::hide_meter ()
2341 gm.get_level_meter().hide_meters ();
2345 RouteTimeAxisView::show_meter ()
2351 RouteTimeAxisView::reset_meter ()
2353 if (Config->get_show_track_meters()) {
2354 int meter_width = 3;
2355 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2358 gm.get_level_meter().setup_meters (height - 9, meter_width);
2365 RouteTimeAxisView::clear_meter ()
2367 gm.get_level_meter().clear_meters ();
2371 RouteTimeAxisView::meter_changed ()
2373 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2375 if (_route && !no_redraw) {
2378 // reset peak when meter point changes
2379 gm.reset_peak_display();
2383 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2386 if (_route && !no_redraw) {
2392 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2394 using namespace Menu_Helpers;
2396 if (!_underlay_streams.empty()) {
2397 MenuList& parent_items = parent_menu->items();
2398 Menu* gs_menu = manage (new Menu);
2399 gs_menu->set_name ("ArdourContextMenu");
2400 MenuList& gs_items = gs_menu->items();
2402 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2404 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2405 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2406 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2412 RouteTimeAxisView::set_underlay_state()
2414 if (!underlay_xml_node) {
2418 XMLNodeList nlist = underlay_xml_node->children();
2419 XMLNodeConstIterator niter;
2420 XMLNode *child_node;
2422 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2423 child_node = *niter;
2425 if (child_node->name() != "Underlay") {
2429 XMLProperty* prop = child_node->property ("id");
2431 PBD::ID id (prop->value());
2433 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2436 add_underlay(v->view(), false);
2445 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2451 RouteTimeAxisView& other = v->trackview();
2453 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2454 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2455 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2459 _underlay_streams.push_back(v);
2460 other._underlay_mirrors.push_back(this);
2462 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2464 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2466 if (!underlay_xml_node) {
2467 underlay_xml_node = xml_node->add_child("Underlays");
2470 XMLNode* node = underlay_xml_node->add_child("Underlay");
2471 XMLProperty* prop = node->add_property("id");
2472 prop->set_value(v->trackview().route()->id().to_s());
2479 RouteTimeAxisView::remove_underlay (StreamView* v)
2485 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2486 RouteTimeAxisView& other = v->trackview();
2488 if (it != _underlay_streams.end()) {
2489 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2491 if (gm == other._underlay_mirrors.end()) {
2492 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2496 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2498 _underlay_streams.erase(it);
2499 other._underlay_mirrors.erase(gm);
2501 if (underlay_xml_node) {
2502 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2508 RouteTimeAxisView::set_button_names ()
2510 if (_route && _route->solo_safe()) {
2511 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2513 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2515 if (Config->get_solo_control_is_listen_control()) {
2516 switch (Config->get_listen_position()) {
2517 case AfterFaderListen:
2518 solo_button->set_text (_("A"));
2519 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2521 case PreFaderListen:
2522 solo_button->set_text (_("P"));
2523 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2527 solo_button->set_text (_("S"));
2528 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2530 mute_button->set_text (_("M"));
2534 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2536 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2537 if (i != _main_automation_menu_map.end()) {
2541 i = _subplugin_menu_map.find (param);
2542 if (i != _subplugin_menu_map.end()) {
2550 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2552 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2554 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2558 gain_track.reset (new AutomationTimeAxisView (_session,
2559 _route, _route->amp(), c, param,
2564 _route->amp()->describe_parameter(param)));
2567 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2570 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2574 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2576 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2578 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2582 mute_track.reset (new AutomationTimeAxisView (_session,
2583 _route, _route, c, param,
2588 _route->describe_parameter(param)));
2591 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2594 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2598 void add_region_to_list (RegionView* rv, RegionList* l)
2600 l->push_back (rv->region());
2604 RouteTimeAxisView::combine_regions ()
2606 /* as of may 2011, we do not offer uncombine for MIDI tracks
2609 if (!is_audio_track()) {
2617 RegionList selected_regions;
2618 boost::shared_ptr<Playlist> playlist = track()->playlist();
2620 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2622 if (selected_regions.size() < 2) {
2626 playlist->clear_changes ();
2627 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2629 _session->add_command (new StatefulDiffCommand (playlist));
2630 /* make the new region be selected */
2632 return _view->find_view (compound_region);
2636 RouteTimeAxisView::uncombine_regions ()
2638 /* as of may 2011, we do not offer uncombine for MIDI tracks
2640 if (!is_audio_track()) {
2648 RegionList selected_regions;
2649 boost::shared_ptr<Playlist> playlist = track()->playlist();
2651 /* have to grab selected regions first because the uncombine is going
2652 * to change that in the middle of the list traverse
2655 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2657 playlist->clear_changes ();
2659 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2660 playlist->uncombine (*i);
2663 _session->add_command (new StatefulDiffCommand (playlist));
2667 RouteTimeAxisView::state_id() const
2669 return string_compose ("rtav %1", _route->id().to_s());
2674 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2676 TimeAxisView::remove_child (c);
2678 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2680 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2681 if (i->second == a) {
2682 _automation_tracks.erase (i);