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);
114 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
118 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
120 RouteUI::set_route (rt);
122 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
123 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
124 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
127 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
130 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
131 gm.get_level_meter().set_no_show_all();
132 gm.get_level_meter().setup_meters(50, meter_width);
133 gm.update_gain_sensitive ();
135 string str = gui_property ("height");
137 set_height (atoi (str));
139 set_height (preset_height (HeightNormal));
142 if (!_route->is_auditioner()) {
143 if (gui_property ("visible").empty()) {
144 set_gui_property ("visible", true);
147 set_gui_property ("visible", false);
151 update_solo_display ();
153 timestretch_rect = 0;
156 ignore_toggle = false;
158 route_group_button.set_name ("route button");
159 playlist_button.set_name ("route button");
160 automation_button.set_name ("route button");
162 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
163 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
164 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
170 switch (track()->mode()) {
172 case ARDOUR::NonLayered:
173 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
175 case ARDOUR::Destructive:
176 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
180 controls_table.attach (*rec_enable_button, 0, 1, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
182 if (is_midi_track()) {
183 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
184 gm.set_fader_name ("MidiTrackFader");
186 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
187 gm.set_fader_name ("AudioTrackFader");
190 rec_enable_button->set_sensitive (_session->writable());
192 /* set playlist button tip to the current playlist, and make it update when it changes */
193 update_playlist_tip ();
194 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
197 gm.set_fader_name ("AudioBusFader");
200 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
201 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
202 top_hbox.pack_end(*mtrbox, false, false, 4);
205 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
206 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
207 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
208 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
210 controls_table.attach (*mute_button, 1, 2, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
212 if (!_route->is_master()) {
213 controls_table.attach (*solo_button, 2, 3, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
216 if (!ARDOUR::Profile->get_trx()) {
217 controls_table.attach (route_group_button, 2, 3, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
218 name_table.attach (gm.get_gain_slider(), 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 2);
221 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
222 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
223 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
225 if (is_midi_track()) {
226 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
228 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
231 update_track_number_visibility();
234 if (!ARDOUR::Profile->get_trx()) {
235 controls_table.attach (automation_button, 1, 2, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
238 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
239 controls_table.attach (playlist_button, 0, 1, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
244 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
245 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
249 str = gui_property ("layer-display");
251 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
254 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
255 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
257 /* pick up the correct freeze state */
262 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
263 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
265 PropertyList* plist = new PropertyList();
267 plist->add (ARDOUR::Properties::mute, true);
268 plist->add (ARDOUR::Properties::solo, true);
270 route_group_menu = new RouteGroupMenu (_session, plist);
272 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
274 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
277 RouteTimeAxisView::~RouteTimeAxisView ()
279 CatchDeletion (this);
281 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
285 delete playlist_action_menu;
286 playlist_action_menu = 0;
291 _automation_tracks.clear ();
293 delete route_group_menu;
297 RouteTimeAxisView::post_construct ()
299 /* map current state of the route */
301 update_diskstream_display ();
302 setup_processor_menu_and_curves ();
303 reset_processor_automation_curves ();
306 /** Set up the processor menu for the current set of processors, and
307 * display automation curves for any parameters which have data.
310 RouteTimeAxisView::setup_processor_menu_and_curves ()
312 _subplugin_menu_map.clear ();
313 subplugin_menu.items().clear ();
314 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
315 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
319 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
321 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
322 if (_route->route_group()) {
323 _route->route_group()->remove (_route);
329 r.push_back (route ());
331 route_group_menu->build (r);
332 route_group_menu->menu()->popup (ev->button, ev->time);
338 RouteTimeAxisView::playlist_changed ()
344 RouteTimeAxisView::label_view ()
346 string x = _route->name ();
347 if (x != name_label.get_text ()) {
348 name_label.set_text (x);
350 const int64_t track_number = _route->track_number ();
351 if (track_number == 0) {
352 number_label.set_text ("");
354 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
359 RouteTimeAxisView::update_track_number_visibility ()
361 bool show_label = _session->config.get_track_name_number();
363 if (_route && _route->is_master()) {
367 if (number_label.get_parent()) {
368 name_hbox.remove (number_label);
371 // controls_table.resize ( 2, 4 );
372 name_hbox.pack_start(number_label, false, false, 2);
373 // controls_table.attach (number_label, 3, 4, 0, 1, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
374 number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
375 number_label.show ();
377 // controls_table.resize ( 2, 3 );
378 number_label.hide ();
383 RouteTimeAxisView::parameter_changed (string const & p)
385 if (p == "track-name-number") {
386 update_track_number_visibility();
391 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
393 if (what_changed.contains (ARDOUR::Properties::name)) {
399 RouteTimeAxisView::take_name_changed (void *src)
407 RouteTimeAxisView::playlist_click ()
409 build_playlist_menu ();
410 conditionally_add_to_selection ();
411 playlist_action_menu->popup (1, gtk_get_current_event_time());
415 RouteTimeAxisView::automation_click ()
417 conditionally_add_to_selection ();
418 build_automation_action_menu (false);
419 automation_action_menu->popup (1, gtk_get_current_event_time());
423 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
425 using namespace Menu_Helpers;
427 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
428 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
431 detach_menu (subplugin_menu);
433 _main_automation_menu_map.clear ();
434 delete automation_action_menu;
435 automation_action_menu = new Menu;
437 MenuList& items = automation_action_menu->items();
439 automation_action_menu->set_name ("ArdourContextMenu");
441 items.push_back (MenuElem (_("Show All Automation"),
442 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
444 items.push_back (MenuElem (_("Show Existing Automation"),
445 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
447 items.push_back (MenuElem (_("Hide All Automation"),
448 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
450 /* Attach the plugin submenu. It may have previously been used elsewhere,
451 so it was detached above
454 if (!subplugin_menu.items().empty()) {
455 items.push_back (SeparatorElem ());
456 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
457 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
462 RouteTimeAxisView::build_display_menu ()
464 using namespace Menu_Helpers;
468 TimeAxisView::build_display_menu ();
470 /* now fill it with our stuff */
472 MenuList& items = display_menu->items();
473 display_menu->set_name ("ArdourContextMenu");
475 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
477 items.push_back (MenuElem (_("Comments..."), sigc::mem_fun (*this, &RouteUI::open_comment_editor)));
479 items.push_back (MenuElem (_("Inputs..."), sigc::mem_fun (*this, &RouteUI::edit_input_configuration)));
481 items.push_back (MenuElem (_("Outputs..."), sigc::mem_fun (*this, &RouteUI::edit_output_configuration)));
483 items.push_back (SeparatorElem());
486 detach_menu (*_size_menu);
489 items.push_back (MenuElem (_("Height"), *_size_menu));
491 items.push_back (SeparatorElem());
493 if (!Profile->get_sae()) {
494 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
495 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
496 items.push_back (SeparatorElem());
499 // Hook for derived classes to add type specific stuff
500 append_extra_display_menu_items ();
504 Menu* layers_menu = manage (new Menu);
505 MenuList &layers_items = layers_menu->items();
506 layers_menu->set_name("ArdourContextMenu");
508 RadioMenuItem::Group layers_group;
510 /* Find out how many overlaid/stacked tracks we have in the selection */
514 TrackSelection const & s = _editor.get_selection().tracks;
515 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
516 StreamView* v = (*i)->view ();
521 switch (v->layer_display ()) {
532 /* We're not connecting to signal_toggled() here; in the case where these two items are
533 set to be in the `inconsistent' state, it seems that one or other will end up active
534 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
535 select the active one, no toggled signal is emitted so nothing happens.
538 _ignore_set_layer_display = true;
540 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
541 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
542 i->set_active (overlaid != 0 && stacked == 0);
543 i->set_inconsistent (overlaid != 0 && stacked != 0);
544 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
546 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
547 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
548 i->set_active (overlaid == 0 && stacked != 0);
549 i->set_inconsistent (overlaid != 0 && stacked != 0);
550 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
552 _ignore_set_layer_display = false;
554 items.push_back (MenuElem (_("Layers"), *layers_menu));
556 if (!Profile->get_sae()) {
558 Menu* alignment_menu = manage (new Menu);
559 MenuList& alignment_items = alignment_menu->items();
560 alignment_menu->set_name ("ArdourContextMenu");
562 RadioMenuItem::Group align_group;
564 /* Same verbose hacks as for the layering options above */
570 boost::shared_ptr<Track> first_track;
572 TrackSelection const & s = _editor.get_selection().tracks;
573 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
574 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
575 if (!r || !r->is_track ()) {
580 first_track = r->track();
583 switch (r->track()->alignment_choice()) {
587 switch (r->track()->alignment_style()) {
588 case ExistingMaterial:
596 case UseExistingMaterial:
612 inconsistent = false;
621 if (!inconsistent && first_track) {
623 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
624 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
625 i->set_active (automatic != 0 && existing == 0 && capture == 0);
626 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
628 switch (first_track->alignment_choice()) {
630 switch (first_track->alignment_style()) {
631 case ExistingMaterial:
632 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
635 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
643 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
644 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
645 i->set_active (existing != 0 && capture == 0 && automatic == 0);
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
648 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
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, UseCaptureTime, true));
653 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
659 Menu* mode_menu = manage (new Menu);
660 MenuList& mode_items = mode_menu->items ();
661 mode_menu->set_name ("ArdourContextMenu");
663 RadioMenuItem::Group mode_group;
669 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
670 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
671 if (!r || !r->is_track ()) {
675 switch (r->track()->mode()) {
688 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
689 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
690 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
691 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
692 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
694 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
695 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
696 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
697 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
698 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
700 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
701 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
702 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
703 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
704 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
706 items.push_back (MenuElem (_("Mode"), *mode_menu));
710 items.push_back (SeparatorElem());
712 build_playlist_menu ();
713 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
714 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
717 route_group_menu->detach ();
720 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
721 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
723 r.push_back (rtv->route ());
728 r.push_back (route ());
731 route_group_menu->build (r);
732 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
734 build_automation_action_menu (true);
735 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
737 items.push_back (SeparatorElem());
741 TrackSelection const & s = _editor.get_selection().tracks;
742 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
743 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
748 if (r->route()->active()) {
755 items.push_back (CheckMenuElem (_("Active")));
756 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
757 bool click_sets_active = true;
758 if (active > 0 && inactive == 0) {
759 i->set_active (true);
760 click_sets_active = false;
761 } else if (active > 0 && inactive > 0) {
762 i->set_inconsistent (true);
764 i->set_sensitive(! _session->transport_rolling());
765 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
767 items.push_back (SeparatorElem());
768 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
769 if (!Profile->get_sae()) {
770 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
772 items.push_front (SeparatorElem());
773 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
778 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
780 if (apply_to_selection) {
781 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
784 bool needs_bounce = false;
786 if (!track()->can_use_mode (mode, needs_bounce)) {
792 cerr << "would bounce this one\n";
797 track()->set_mode (mode);
799 rec_enable_button->remove ();
802 case ARDOUR::NonLayered:
804 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
805 rec_enable_button->set_text (string());
807 case ARDOUR::Destructive:
808 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
809 rec_enable_button->set_text (string());
813 rec_enable_button->show_all ();
818 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
820 TimeAxisView::show_timestretch (start, end, layers, layer);
830 /* check that the time selection was made in our route, or our route group.
831 remember that route_group() == 0 implies the route is *not* in a edit group.
834 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
835 /* this doesn't apply to us */
839 /* ignore it if our edit group is not active */
841 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
846 if (timestretch_rect == 0) {
847 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
848 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
849 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
852 timestretch_rect->show ();
853 timestretch_rect->raise_to_top ();
855 double const x1 = start / _editor.get_current_zoom();
856 double const x2 = (end - 1) / _editor.get_current_zoom();
858 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
859 x2, current_height() * (layers - layer) / layers));
863 RouteTimeAxisView::hide_timestretch ()
865 TimeAxisView::hide_timestretch ();
867 if (timestretch_rect) {
868 timestretch_rect->hide ();
873 RouteTimeAxisView::show_selection (TimeSelection& ts)
877 /* ignore it if our edit group is not active or if the selection was started
878 in some other track or route group (remember that route_group() == 0 means
879 that the track is not in an route group).
882 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
883 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
889 TimeAxisView::show_selection (ts);
893 RouteTimeAxisView::set_height (uint32_t h)
896 bool height_changed = (height == 0) || (h != height);
899 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
902 gm.get_level_meter().setup_meters (gmlen, meter_width);
904 TimeAxisView::set_height (h);
907 _view->set_height ((double) current_height());
910 if (height >= preset_height (HeightNormal)) {
914 gm.get_gain_slider().show();
916 if (!_route || _route->is_monitor()) {
921 if (rec_enable_button)
922 rec_enable_button->show();
924 route_group_button.show();
925 automation_button.show();
927 if (is_track() && track()->mode() == ARDOUR::Normal) {
928 playlist_button.show();
935 gm.get_gain_slider().hide();
937 if (!_route || _route->is_monitor()) {
942 if (rec_enable_button)
943 rec_enable_button->show();
945 route_group_button.hide ();
946 automation_button.hide ();
948 if (is_track() && track()->mode() == ARDOUR::Normal) {
949 playlist_button.hide ();
954 if (height_changed && !no_redraw) {
955 /* only emit the signal if the height really changed */
961 RouteTimeAxisView::route_color_changed ()
964 _view->apply_color (color(), StreamView::RegionColor);
967 number_label.set_fixed_colors (gdk_color_to_rgba (color()), gdk_color_to_rgba (color()));
971 RouteTimeAxisView::reset_samples_per_pixel ()
973 set_samples_per_pixel (_editor.get_current_zoom());
977 RouteTimeAxisView::set_samples_per_pixel (double fpp)
982 speed = track()->speed();
986 _view->set_samples_per_pixel (fpp * speed);
989 TimeAxisView::set_samples_per_pixel (fpp * speed);
993 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
995 if (!mitem->get_active()) {
996 /* this is one of the two calls made when these radio menu items change status. this one
997 is for the item that became inactive, and we want to ignore it.
1002 if (apply_to_selection) {
1003 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1006 track()->set_align_choice (choice);
1012 RouteTimeAxisView::rename_current_playlist ()
1014 ArdourPrompter prompter (true);
1017 boost::shared_ptr<Track> tr = track();
1018 if (!tr || tr->destructive()) {
1022 boost::shared_ptr<Playlist> pl = tr->playlist();
1027 prompter.set_title (_("Rename Playlist"));
1028 prompter.set_prompt (_("New name for playlist:"));
1029 prompter.set_initial_text (pl->name());
1030 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1031 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1033 switch (prompter.run ()) {
1034 case Gtk::RESPONSE_ACCEPT:
1035 prompter.get_result (name);
1036 if (name.length()) {
1037 pl->set_name (name);
1047 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1049 std::string ret (basename);
1051 std::string const group_string = "." + route_group()->name() + ".";
1053 // iterate through all playlists
1055 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1056 std::string tmp = (*i)->name();
1058 std::string::size_type idx = tmp.find(group_string);
1059 // find those which belong to this group
1060 if (idx != string::npos) {
1061 tmp = tmp.substr(idx + group_string.length());
1063 // and find the largest current number
1065 if (x > maxnumber) {
1074 snprintf (buf, sizeof(buf), "%d", maxnumber);
1076 ret = this->name() + "." + route_group()->name () + "." + buf;
1082 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1086 boost::shared_ptr<Track> tr = track ();
1087 if (!tr || tr->destructive()) {
1091 boost::shared_ptr<const Playlist> pl = tr->playlist();
1098 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1099 name = resolve_new_group_playlist_name(name, playlists_before_op);
1102 while (_session->playlists->by_name(name)) {
1103 name = Playlist::bump_name (name, *_session);
1106 // TODO: The prompter "new" button should be de-activated if the user
1107 // specifies a playlist name which already exists in the session.
1111 ArdourPrompter prompter (true);
1113 prompter.set_title (_("New Copy Playlist"));
1114 prompter.set_prompt (_("Name for new playlist:"));
1115 prompter.set_initial_text (name);
1116 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1117 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1118 prompter.show_all ();
1120 switch (prompter.run ()) {
1121 case Gtk::RESPONSE_ACCEPT:
1122 prompter.get_result (name);
1130 if (name.length()) {
1131 tr->use_copy_playlist ();
1132 tr->playlist()->set_name (name);
1137 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1141 boost::shared_ptr<Track> tr = track ();
1142 if (!tr || tr->destructive()) {
1146 boost::shared_ptr<const Playlist> pl = tr->playlist();
1153 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1154 name = resolve_new_group_playlist_name(name,playlists_before_op);
1157 while (_session->playlists->by_name(name)) {
1158 name = Playlist::bump_name (name, *_session);
1164 ArdourPrompter prompter (true);
1166 prompter.set_title (_("New Playlist"));
1167 prompter.set_prompt (_("Name for new playlist:"));
1168 prompter.set_initial_text (name);
1169 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1170 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1172 switch (prompter.run ()) {
1173 case Gtk::RESPONSE_ACCEPT:
1174 prompter.get_result (name);
1182 if (name.length()) {
1183 tr->use_new_playlist ();
1184 tr->playlist()->set_name (name);
1189 RouteTimeAxisView::clear_playlist ()
1191 boost::shared_ptr<Track> tr = track ();
1192 if (!tr || tr->destructive()) {
1196 boost::shared_ptr<Playlist> pl = tr->playlist();
1201 _editor.clear_playlist (pl);
1205 RouteTimeAxisView::speed_changed ()
1207 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1211 RouteTimeAxisView::update_diskstream_display ()
1221 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1223 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1225 /* special case: select/deselect all tracks */
1226 if (_editor.get_selection().selected (this)) {
1227 _editor.get_selection().clear_tracks ();
1229 _editor.select_all_tracks ();
1235 switch (ArdourKeyboard::selection_type (ev->state)) {
1236 case Selection::Toggle:
1237 _editor.get_selection().toggle (this);
1240 case Selection::Set:
1241 _editor.get_selection().set (this);
1244 case Selection::Extend:
1245 _editor.extend_selection_to_track (*this);
1248 case Selection::Add:
1249 _editor.get_selection().add (this);
1255 RouteTimeAxisView::set_selected_points (PointSelection& points)
1257 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1258 (*i)->set_selected_points (points);
1263 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1266 _view->set_selected_regionviews (regions);
1270 /** Add the selectable things that we have to a list.
1271 * @param results List to add things to.
1274 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1279 speed = track()->speed();
1282 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1283 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1285 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1286 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1289 /* pick up visible automation tracks */
1291 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1292 if (!(*i)->hidden()) {
1293 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1299 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1302 _view->get_inverted_selectables (sel, results);
1305 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1306 if (!(*i)->hidden()) {
1307 (*i)->get_inverted_selectables (sel, results);
1315 RouteTimeAxisView::route_group () const
1317 return _route->route_group();
1321 RouteTimeAxisView::name() const
1323 return _route->name();
1326 boost::shared_ptr<Playlist>
1327 RouteTimeAxisView::playlist () const
1329 boost::shared_ptr<Track> tr;
1331 if ((tr = track()) != 0) {
1332 return tr->playlist();
1334 return boost::shared_ptr<Playlist> ();
1339 RouteTimeAxisView::name_entry_changed ()
1341 TimeAxisView::name_entry_changed ();
1343 string x = name_entry->get_text ();
1345 if (x == _route->name()) {
1349 strip_whitespace_edges (x);
1351 if (x.length() == 0) {
1352 name_entry->set_text (_route->name());
1356 if (_session->route_name_internal (x)) {
1357 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1359 name_entry->grab_focus ();
1360 } else if (RouteUI::verify_new_route_name (x)) {
1361 _route->set_name (x);
1363 name_entry->grab_focus ();
1367 boost::shared_ptr<Region>
1368 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1370 boost::shared_ptr<Playlist> pl = playlist ();
1373 return pl->find_next_region (pos, point, dir);
1376 return boost::shared_ptr<Region> ();
1380 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1382 boost::shared_ptr<Playlist> pl = playlist ();
1385 return pl->find_next_region_boundary (pos, dir);
1392 RouteTimeAxisView::fade_range (TimeSelection& selection)
1394 boost::shared_ptr<Playlist> what_we_got;
1395 boost::shared_ptr<Track> tr = track ();
1396 boost::shared_ptr<Playlist> playlist;
1399 /* route is a bus, not a track */
1403 playlist = tr->playlist();
1405 TimeSelection time (selection);
1406 float const speed = tr->speed();
1407 if (speed != 1.0f) {
1408 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1409 (*i).start = session_frame_to_track_frame((*i).start, speed);
1410 (*i).end = session_frame_to_track_frame((*i).end, speed);
1414 playlist->clear_changes ();
1415 playlist->clear_owned_changes ();
1417 playlist->fade_range (time);
1419 vector<Command*> cmds;
1420 playlist->rdiff (cmds);
1421 _session->add_commands (cmds);
1422 _session->add_command (new StatefulDiffCommand (playlist));
1427 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1429 boost::shared_ptr<Playlist> what_we_got;
1430 boost::shared_ptr<Track> tr = track ();
1431 boost::shared_ptr<Playlist> playlist;
1434 /* route is a bus, not a track */
1438 playlist = tr->playlist();
1440 TimeSelection time (selection.time);
1441 float const speed = tr->speed();
1442 if (speed != 1.0f) {
1443 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1444 (*i).start = session_frame_to_track_frame((*i).start, speed);
1445 (*i).end = session_frame_to_track_frame((*i).end, speed);
1449 playlist->clear_changes ();
1450 playlist->clear_owned_changes ();
1454 if (playlist->cut (time) != 0) {
1455 if (Config->get_edit_mode() == Ripple)
1456 playlist->ripple(time.start(), -time.length(), NULL);
1457 // no need to exclude any regions from rippling here
1459 vector<Command*> cmds;
1460 playlist->rdiff (cmds);
1461 _session->add_commands (cmds);
1463 _session->add_command (new StatefulDiffCommand (playlist));
1468 if ((what_we_got = playlist->cut (time)) != 0) {
1469 _editor.get_cut_buffer().add (what_we_got);
1470 if (Config->get_edit_mode() == Ripple)
1471 playlist->ripple(time.start(), -time.length(), NULL);
1472 // no need to exclude any regions from rippling here
1474 vector<Command*> cmds;
1475 playlist->rdiff (cmds);
1476 _session->add_commands (cmds);
1478 _session->add_command (new StatefulDiffCommand (playlist));
1482 if ((what_we_got = playlist->copy (time)) != 0) {
1483 _editor.get_cut_buffer().add (what_we_got);
1488 if ((what_we_got = playlist->cut (time)) != 0) {
1489 if (Config->get_edit_mode() == Ripple)
1490 playlist->ripple(time.start(), -time.length(), NULL);
1491 // no need to exclude any regions from rippling here
1493 vector<Command*> cmds;
1494 playlist->rdiff (cmds);
1495 _session->add_commands (cmds);
1496 _session->add_command (new StatefulDiffCommand (playlist));
1497 what_we_got->release ();
1504 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1510 boost::shared_ptr<Playlist> pl = playlist ();
1511 PlaylistSelection::iterator p;
1513 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1515 if (p == selection.playlists.end()) {
1519 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1521 if (track()->speed() != 1.0f) {
1522 pos = session_frame_to_track_frame (pos, track()->speed());
1523 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1526 pl->clear_changes ();
1527 if (Config->get_edit_mode() == Ripple) {
1528 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
1529 framecnt_t amount = extent.second - extent.first;
1530 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1532 pl->paste (*p, pos, times);
1534 vector<Command*> cmds;
1536 _session->add_commands (cmds);
1538 _session->add_command (new StatefulDiffCommand (pl));
1544 struct PlaylistSorter {
1545 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1546 return a->sort_id() < b->sort_id();
1551 RouteTimeAxisView::build_playlist_menu ()
1553 using namespace Menu_Helpers;
1559 delete playlist_action_menu;
1560 playlist_action_menu = new Menu;
1561 playlist_action_menu->set_name ("ArdourContextMenu");
1563 MenuList& playlist_items = playlist_action_menu->items();
1564 playlist_action_menu->set_name ("ArdourContextMenu");
1565 playlist_items.clear();
1567 RadioMenuItem::Group playlist_group;
1568 boost::shared_ptr<Track> tr = track ();
1570 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1572 /* sort the playlists */
1574 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1576 /* add the playlists to the menu */
1577 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1578 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1579 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1580 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1582 if (tr->playlist()->id() == (*i)->id()) {
1588 playlist_items.push_back (SeparatorElem());
1589 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1590 playlist_items.push_back (SeparatorElem());
1592 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1593 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1594 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1597 // Use a label which tells the user what is happening
1598 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1599 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1603 playlist_items.push_back (SeparatorElem());
1604 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1605 playlist_items.push_back (SeparatorElem());
1607 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1611 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1613 assert (is_track());
1615 // exit if we were triggered by deactivating the old playlist
1616 if (!item->get_active()) {
1620 boost::shared_ptr<Playlist> pl (wpl.lock());
1626 if (track()->playlist() == pl) {
1627 // exit when use_playlist is called by the creation of the playlist menu
1628 // or the playlist choice is unchanged
1632 track()->use_playlist (pl);
1634 RouteGroup* rg = route_group();
1636 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1637 std::string group_string = "." + rg->name() + ".";
1639 std::string take_name = pl->name();
1640 std::string::size_type idx = take_name.find(group_string);
1642 if (idx == std::string::npos)
1645 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1647 boost::shared_ptr<RouteList> rl (rg->route_list());
1649 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1650 if ((*i) == this->route()) {
1654 std::string playlist_name = (*i)->name()+group_string+take_name;
1656 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1661 if (track->freeze_state() == Track::Frozen) {
1662 /* Don't change playlists of frozen tracks */
1666 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1668 // No playlist for this track for this take yet, make it
1669 track->use_new_playlist();
1670 track->playlist()->set_name(playlist_name);
1672 track->use_playlist(ipl);
1679 RouteTimeAxisView::update_playlist_tip ()
1681 RouteGroup* rg = route_group ();
1682 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1683 string group_string = "." + rg->name() + ".";
1685 string take_name = track()->playlist()->name();
1686 string::size_type idx = take_name.find(group_string);
1688 if (idx != string::npos) {
1689 /* find the bit containing the take number / name */
1690 take_name = take_name.substr (idx + group_string.length());
1692 /* set the playlist button tooltip to the take name */
1693 ARDOUR_UI::instance()->set_tip (
1695 string_compose(_("Take: %1.%2"),
1696 Glib::Markup::escape_text(rg->name()),
1697 Glib::Markup::escape_text(take_name))
1704 /* set the playlist button tooltip to the playlist name */
1705 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1710 RouteTimeAxisView::show_playlist_selector ()
1712 _editor.playlist_selector().show_for (this);
1716 RouteTimeAxisView::map_frozen ()
1722 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1724 switch (track()->freeze_state()) {
1726 playlist_button.set_sensitive (false);
1727 rec_enable_button->set_sensitive (false);
1730 playlist_button.set_sensitive (true);
1731 rec_enable_button->set_sensitive (true);
1737 RouteTimeAxisView::color_handler ()
1739 //case cTimeStretchOutline:
1740 if (timestretch_rect) {
1741 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1743 //case cTimeStretchFill:
1744 if (timestretch_rect) {
1745 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1751 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1752 * Will add track if necessary.
1755 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1757 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1758 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1761 /* it doesn't exist yet, so we don't care about the button state: just add it */
1762 create_automation_child (param, true);
1765 bool yn = menu->get_active();
1766 bool changed = false;
1768 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1770 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1771 will have done that for us.
1774 if (changed && !no_redraw) {
1782 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1784 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1790 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1792 if (menu && !_hidden) {
1793 ignore_toggle = true;
1794 menu->set_active (false);
1795 ignore_toggle = false;
1798 if (_route && !no_redraw) {
1805 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1807 if (apply_to_selection) {
1808 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1812 /* Show our automation */
1814 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1815 i->second->set_marked_for_display (true);
1817 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1820 menu->set_active(true);
1825 /* Show processor automation */
1827 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1828 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1829 if ((*ii)->view == 0) {
1830 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1833 (*ii)->menu_item->set_active (true);
1846 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1848 if (apply_to_selection) {
1849 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1853 /* Show our automation */
1855 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1856 if (i->second->has_automation()) {
1857 i->second->set_marked_for_display (true);
1859 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1861 menu->set_active(true);
1866 /* Show processor automation */
1868 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1869 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1870 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1871 (*ii)->menu_item->set_active (true);
1883 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1885 if (apply_to_selection) {
1886 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1890 /* Hide our automation */
1892 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1893 i->second->set_marked_for_display (false);
1895 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1898 menu->set_active (false);
1902 /* Hide processor automation */
1904 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1905 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1906 (*ii)->menu_item->set_active (false);
1917 RouteTimeAxisView::region_view_added (RegionView* rv)
1919 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1920 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1921 boost::shared_ptr<AutomationTimeAxisView> atv;
1923 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1928 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1929 (*i)->add_ghost(rv);
1933 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1935 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1941 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1943 parent.remove_processor_automation_node (this);
1947 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1950 remove_child (pan->view);
1954 RouteTimeAxisView::ProcessorAutomationNode*
1955 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1957 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1959 if ((*i)->processor == processor) {
1961 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1962 if ((*ii)->what == what) {
1972 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1974 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1977 ProcessorAutomationNode* pan;
1979 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1980 /* session state may never have been saved with new plugin */
1981 error << _("programming error: ")
1982 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1983 processor->name(), what.type(), (int) what.channel(), what.id() )
1993 boost::shared_ptr<AutomationControl> control
1994 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1996 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1997 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1998 _editor, *this, false, parent_canvas,
1999 processor->describe_parameter (what), processor->name()));
2001 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
2003 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2006 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2011 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2014 pan->menu_item->set_active (false);
2023 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2025 boost::shared_ptr<Processor> processor (p.lock ());
2027 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2028 /* The Amp processor is a special case and is dealt with separately */
2032 set<Evoral::Parameter> existing;
2034 processor->what_has_data (existing);
2036 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2038 Evoral::Parameter param (*i);
2039 boost::shared_ptr<AutomationLine> al;
2041 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2044 add_processor_automation_curve (processor, param);
2050 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2052 using namespace Menu_Helpers;
2056 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2058 _automation_tracks[param] = track;
2060 /* existing state overrides "show" argument */
2061 string s = track->gui_property ("visible");
2063 show = string_is_affirmative (s);
2066 /* this might or might not change the visibility status, so don't rely on it */
2067 track->set_marked_for_display (show);
2069 if (show && !no_redraw) {
2073 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2074 /* MIDI-related parameters are always in the menu, there's no
2075 reason to rebuild the menu just because we added a automation
2076 lane for one of them. But if we add a non-MIDI automation
2077 lane, then we need to invalidate the display menu.
2079 delete display_menu;
2085 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2087 boost::shared_ptr<Processor> processor (p.lock ());
2089 if (!processor || !processor->display_to_user ()) {
2093 /* we use this override to veto the Amp processor from the plugin menu,
2094 as its automation lane can be accessed using the special "Fader" menu
2098 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2102 using namespace Menu_Helpers;
2103 ProcessorAutomationInfo *rai;
2104 list<ProcessorAutomationInfo*>::iterator x;
2106 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2108 if (automatable.empty()) {
2112 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2113 if ((*x)->processor == processor) {
2118 if (x == processor_automation.end()) {
2120 rai = new ProcessorAutomationInfo (processor);
2121 processor_automation.push_back (rai);
2129 /* any older menu was deleted at the top of processors_changed()
2130 when we cleared the subplugin menu.
2133 rai->menu = manage (new Menu);
2134 MenuList& items = rai->menu->items();
2135 rai->menu->set_name ("ArdourContextMenu");
2139 std::set<Evoral::Parameter> has_visible_automation;
2140 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2142 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2144 ProcessorAutomationNode* pan;
2145 Gtk::CheckMenuItem* mitem;
2147 string name = processor->describe_parameter (*i);
2149 items.push_back (CheckMenuElem (name));
2150 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2152 _subplugin_menu_map[*i] = mitem;
2154 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2155 mitem->set_active(true);
2158 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2162 pan = new ProcessorAutomationNode (*i, mitem, *this);
2164 rai->lines.push_back (pan);
2168 pan->menu_item = mitem;
2172 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2175 /* add the menu for this processor, because the subplugin
2176 menu is always cleared at the top of processors_changed().
2177 this is the result of some poor design in gtkmm and/or
2181 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2186 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2187 RouteTimeAxisView::ProcessorAutomationNode* pan)
2189 bool showit = pan->menu_item->get_active();
2190 bool redraw = false;
2192 if (pan->view == 0 && showit) {
2193 add_processor_automation_curve (rai->processor, pan->what);
2197 if (pan->view && pan->view->set_marked_for_display (showit)) {
2201 if (redraw && !no_redraw) {
2207 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2209 if (c.type == RouteProcessorChange::MeterPointChange) {
2210 /* nothing to do if only the meter point has changed */
2214 using namespace Menu_Helpers;
2216 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2217 (*i)->valid = false;
2220 setup_processor_menu_and_curves ();
2222 bool deleted_processor_automation = false;
2224 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2226 list<ProcessorAutomationInfo*>::iterator tmp;
2234 processor_automation.erase (i);
2235 deleted_processor_automation = true;
2242 if (deleted_processor_automation && !no_redraw) {
2247 boost::shared_ptr<AutomationLine>
2248 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2250 ProcessorAutomationNode* pan;
2252 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2258 return boost::shared_ptr<AutomationLine>();
2262 RouteTimeAxisView::reset_processor_automation_curves ()
2264 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2270 RouteTimeAxisView::can_edit_name () const
2272 /* we do not allow track name changes if it is record enabled
2274 return !_route->record_enabled();
2278 RouteTimeAxisView::blink_rec_display (bool onoff)
2280 RouteUI::blink_rec_display (onoff);
2284 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2286 if (_ignore_set_layer_display) {
2290 if (apply_to_selection) {
2291 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2295 _view->set_layer_display (d);
2298 set_gui_property (X_("layer-display"), enum_2_string (d));
2303 RouteTimeAxisView::layer_display () const
2306 return _view->layer_display ();
2309 /* we don't know, since we don't have a _view, so just return something */
2315 boost::shared_ptr<AutomationTimeAxisView>
2316 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2318 AutomationTracks::iterator i = _automation_tracks.find(param);
2319 if (i != _automation_tracks.end()) {
2322 return boost::shared_ptr<AutomationTimeAxisView>();
2327 RouteTimeAxisView::fast_update ()
2329 gm.get_level_meter().update_meters ();
2333 RouteTimeAxisView::hide_meter ()
2336 gm.get_level_meter().hide_meters ();
2340 RouteTimeAxisView::show_meter ()
2346 RouteTimeAxisView::reset_meter ()
2348 if (Config->get_show_track_meters()) {
2349 int meter_width = 3;
2350 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2353 gm.get_level_meter().setup_meters (height - 9, meter_width);
2360 RouteTimeAxisView::clear_meter ()
2362 gm.get_level_meter().clear_meters ();
2366 RouteTimeAxisView::meter_changed ()
2368 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2370 if (_route && !no_redraw) {
2373 // reset peak when meter point changes
2374 gm.reset_peak_display();
2378 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2381 if (_route && !no_redraw) {
2387 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2389 using namespace Menu_Helpers;
2391 if (!_underlay_streams.empty()) {
2392 MenuList& parent_items = parent_menu->items();
2393 Menu* gs_menu = manage (new Menu);
2394 gs_menu->set_name ("ArdourContextMenu");
2395 MenuList& gs_items = gs_menu->items();
2397 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2399 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2400 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2401 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2407 RouteTimeAxisView::set_underlay_state()
2409 if (!underlay_xml_node) {
2413 XMLNodeList nlist = underlay_xml_node->children();
2414 XMLNodeConstIterator niter;
2415 XMLNode *child_node;
2417 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2418 child_node = *niter;
2420 if (child_node->name() != "Underlay") {
2424 XMLProperty* prop = child_node->property ("id");
2426 PBD::ID id (prop->value());
2428 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2431 add_underlay(v->view(), false);
2440 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2446 RouteTimeAxisView& other = v->trackview();
2448 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2449 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2450 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2454 _underlay_streams.push_back(v);
2455 other._underlay_mirrors.push_back(this);
2457 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2459 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2461 if (!underlay_xml_node) {
2462 underlay_xml_node = xml_node->add_child("Underlays");
2465 XMLNode* node = underlay_xml_node->add_child("Underlay");
2466 XMLProperty* prop = node->add_property("id");
2467 prop->set_value(v->trackview().route()->id().to_s());
2474 RouteTimeAxisView::remove_underlay (StreamView* v)
2480 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2481 RouteTimeAxisView& other = v->trackview();
2483 if (it != _underlay_streams.end()) {
2484 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2486 if (gm == other._underlay_mirrors.end()) {
2487 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2491 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2493 _underlay_streams.erase(it);
2494 other._underlay_mirrors.erase(gm);
2496 if (underlay_xml_node) {
2497 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2503 RouteTimeAxisView::set_button_names ()
2505 if (_route && _route->solo_safe()) {
2506 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2508 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2510 if (Config->get_solo_control_is_listen_control()) {
2511 switch (Config->get_listen_position()) {
2512 case AfterFaderListen:
2513 solo_button->set_text (_("A"));
2514 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2516 case PreFaderListen:
2517 solo_button->set_text (_("P"));
2518 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2522 solo_button->set_text (_("S"));
2523 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2525 mute_button->set_text (_("M"));
2529 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2531 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2532 if (i != _main_automation_menu_map.end()) {
2536 i = _subplugin_menu_map.find (param);
2537 if (i != _subplugin_menu_map.end()) {
2545 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2547 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2549 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2553 gain_track.reset (new AutomationTimeAxisView (_session,
2554 _route, _route->amp(), c, param,
2559 _route->amp()->describe_parameter(param)));
2562 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2565 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2569 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2571 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2573 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2577 mute_track.reset (new AutomationTimeAxisView (_session,
2578 _route, _route, c, param,
2583 _route->describe_parameter(param)));
2586 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2589 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2593 void add_region_to_list (RegionView* rv, RegionList* l)
2595 l->push_back (rv->region());
2599 RouteTimeAxisView::combine_regions ()
2601 /* as of may 2011, we do not offer uncombine for MIDI tracks
2604 if (!is_audio_track()) {
2612 RegionList selected_regions;
2613 boost::shared_ptr<Playlist> playlist = track()->playlist();
2615 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2617 if (selected_regions.size() < 2) {
2621 playlist->clear_changes ();
2622 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2624 _session->add_command (new StatefulDiffCommand (playlist));
2625 /* make the new region be selected */
2627 return _view->find_view (compound_region);
2631 RouteTimeAxisView::uncombine_regions ()
2633 /* as of may 2011, we do not offer uncombine for MIDI tracks
2635 if (!is_audio_track()) {
2643 RegionList selected_regions;
2644 boost::shared_ptr<Playlist> playlist = track()->playlist();
2646 /* have to grab selected regions first because the uncombine is going
2647 * to change that in the middle of the list traverse
2650 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2652 playlist->clear_changes ();
2654 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2655 playlist->uncombine (*i);
2658 _session->add_command (new StatefulDiffCommand (playlist));
2662 RouteTimeAxisView::state_id() const
2664 return string_compose ("rtav %1", _route->id().to_s());
2669 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2671 TimeAxisView::remove_child (c);
2673 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2675 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2676 if (i->second == a) {
2677 _automation_tracks.erase (i);