2 Copyright (C) 2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #include <sigc++/bind.h>
31 #include "pbd/error.h"
32 #include "pbd/stl_delete.h"
33 #include "pbd/whitespace.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/enumwriter.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm/menu.h>
39 #include <gtkmm/menuitem.h>
40 #include <gtkmm2ext/gtk_ui.h>
41 #include <gtkmm2ext/selector.h>
42 #include <gtkmm2ext/bindable_button.h>
43 #include <gtkmm2ext/utils.h>
45 #include "ardour/amp.h"
46 #include "ardour/meter.h"
47 #include "ardour/event_type_map.h"
48 #include "ardour/processor.h"
49 #include "ardour/profile.h"
50 #include "ardour/route_group.h"
51 #include "ardour/session.h"
52 #include "ardour/session_playlists.h"
54 #include "evoral/Parameter.hpp"
56 #include "canvas/debug.h"
58 #include "ardour_ui.h"
59 #include "ardour_button.h"
61 #include "global_signals.h"
62 #include "route_time_axis.h"
63 #include "automation_time_axis.h"
65 #include "gui_thread.h"
67 #include "playlist_selector.h"
68 #include "point_selection.h"
70 #include "public_editor.h"
71 #include "region_view.h"
72 #include "rgb_macros.h"
73 #include "selection.h"
74 #include "streamview.h"
76 #include "route_group_menu.h"
78 #include "ardour/track.h"
82 using namespace ARDOUR;
83 using namespace ARDOUR_UI_UTILS;
85 using namespace Gtkmm2ext;
87 using namespace Editing;
91 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, ArdourCanvas::Canvas& canvas)
94 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
96 , parent_canvas (canvas)
99 , route_group_button (_("g"))
100 , playlist_button (_("p"))
101 , automation_button (_("a"))
102 , automation_action_menu (0)
103 , plugins_submenu_item (0)
104 , route_group_menu (0)
105 , playlist_action_menu (0)
107 , color_mode_menu (0)
108 , gm (sess, true, 125, 18)
109 , _ignore_set_layer_display (false)
111 number_label.set_corner_radius(2);
112 number_label.set_name("tracknumber label");
113 number_label.set_alignment(.5, .5);
115 sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
119 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
121 RouteUI::set_route (rt);
123 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
124 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
125 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
128 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
131 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
132 gm.get_level_meter().set_no_show_all();
133 gm.get_level_meter().setup_meters(50, meter_width);
134 gm.update_gain_sensitive ();
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_auditioner()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("route button");
160 playlist_button.set_name ("route button");
161 automation_button.set_name ("route button");
163 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
164 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
165 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 switch (track()->mode()) {
173 case ARDOUR::NonLayered:
174 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
176 case ARDOUR::Destructive:
177 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
181 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
183 if (is_midi_track()) {
184 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
185 gm.set_fader_name ("MidiTrackFader");
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
188 gm.set_fader_name ("AudioTrackFader");
191 rec_enable_button->set_sensitive (_session->writable());
193 /* set playlist button tip to the current playlist, and make it update when it changes */
194 update_playlist_tip ();
195 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
198 gm.set_fader_name ("AudioBusFader");
201 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
202 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
203 controls_hbox.pack_start(*mtrbox, false, false, 4);
206 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
207 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
208 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
209 _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
211 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 if (!_route->is_master()) {
214 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
217 if (!ARDOUR::Profile->get_trx()) {
218 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
219 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
222 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
223 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
224 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
226 if (is_midi_track()) {
227 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
229 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
232 update_track_number_visibility();
235 if (!ARDOUR::Profile->get_trx()) {
236 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
239 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
240 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
245 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
246 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
250 str = gui_property ("layer-display");
252 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
255 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
256 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
258 /* pick up the correct freeze state */
263 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
264 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
266 PropertyList* plist = new PropertyList();
268 plist->add (ARDOUR::Properties::mute, true);
269 plist->add (ARDOUR::Properties::solo, true);
271 route_group_menu = new RouteGroupMenu (_session, plist);
273 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
275 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
278 RouteTimeAxisView::~RouteTimeAxisView ()
280 CatchDeletion (this);
282 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
286 delete playlist_action_menu;
287 playlist_action_menu = 0;
292 _automation_tracks.clear ();
294 delete route_group_menu;
298 RouteTimeAxisView::post_construct ()
300 /* map current state of the route */
302 update_diskstream_display ();
303 setup_processor_menu_and_curves ();
304 reset_processor_automation_curves ();
307 /** Set up the processor menu for the current set of processors, and
308 * display automation curves for any parameters which have data.
311 RouteTimeAxisView::setup_processor_menu_and_curves ()
313 _subplugin_menu_map.clear ();
314 subplugin_menu.items().clear ();
315 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
316 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
320 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
322 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
323 if (_route->route_group()) {
324 _route->route_group()->remove (_route);
330 r.push_back (route ());
332 route_group_menu->build (r);
333 route_group_menu->menu()->popup (ev->button, ev->time);
339 RouteTimeAxisView::playlist_changed ()
345 RouteTimeAxisView::label_view ()
347 string x = _route->name ();
348 if (x != name_label.get_text ()) {
349 name_label.set_text (x);
351 const int64_t track_number = _route->track_number ();
352 if (track_number == 0) {
353 number_label.set_text ("");
355 number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
360 RouteTimeAxisView::update_track_number_visibility ()
362 bool show_label = _session->config.get_track_name_number();
364 if (_route && _route->is_master()) {
368 //if (show_label == number_label.is_visible()) { return; }
369 if (number_label.get_parent()) {
370 controls_table.remove (number_label);
372 if (name_hbox.get_parent()) {
373 controls_table.remove (name_hbox);
376 controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::FILL|Gtk::EXPAND, 3, 0);
377 controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
378 number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
380 number_label.show ();
382 controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
384 number_label.hide ();
389 RouteTimeAxisView::parameter_changed (string const & p)
391 if (p == "track-name-number") {
392 update_track_number_visibility();
397 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
399 if (what_changed.contains (ARDOUR::Properties::name)) {
405 RouteTimeAxisView::take_name_changed (void *src)
413 RouteTimeAxisView::playlist_click ()
415 build_playlist_menu ();
416 conditionally_add_to_selection ();
417 playlist_action_menu->popup (1, gtk_get_current_event_time());
421 RouteTimeAxisView::automation_click ()
423 conditionally_add_to_selection ();
424 build_automation_action_menu (false);
425 automation_action_menu->popup (1, gtk_get_current_event_time());
429 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
431 using namespace Menu_Helpers;
433 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
434 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
437 detach_menu (subplugin_menu);
439 _main_automation_menu_map.clear ();
440 delete automation_action_menu;
441 automation_action_menu = new Menu;
443 MenuList& items = automation_action_menu->items();
445 automation_action_menu->set_name ("ArdourContextMenu");
447 items.push_back (MenuElem (_("Show All Automation"),
448 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
450 items.push_back (MenuElem (_("Show Existing Automation"),
451 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
453 items.push_back (MenuElem (_("Hide All Automation"),
454 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
456 /* Attach the plugin submenu. It may have previously been used elsewhere,
457 so it was detached above
460 if (!subplugin_menu.items().empty()) {
461 items.push_back (SeparatorElem ());
462 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
463 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
468 RouteTimeAxisView::build_display_menu ()
470 using namespace Menu_Helpers;
474 TimeAxisView::build_display_menu ();
476 /* now fill it with our stuff */
478 MenuList& items = display_menu->items();
479 display_menu->set_name ("ArdourContextMenu");
481 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
484 detach_menu (*_size_menu);
487 items.push_back (MenuElem (_("Height"), *_size_menu));
489 items.push_back (SeparatorElem());
491 if (!Profile->get_sae()) {
492 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
493 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
494 items.push_back (SeparatorElem());
497 // Hook for derived classes to add type specific stuff
498 append_extra_display_menu_items ();
502 Menu* layers_menu = manage (new Menu);
503 MenuList &layers_items = layers_menu->items();
504 layers_menu->set_name("ArdourContextMenu");
506 RadioMenuItem::Group layers_group;
508 /* Find out how many overlaid/stacked tracks we have in the selection */
512 TrackSelection const & s = _editor.get_selection().tracks;
513 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
514 StreamView* v = (*i)->view ();
519 switch (v->layer_display ()) {
530 /* We're not connecting to signal_toggled() here; in the case where these two items are
531 set to be in the `inconsistent' state, it seems that one or other will end up active
532 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
533 select the active one, no toggled signal is emitted so nothing happens.
536 _ignore_set_layer_display = true;
538 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
539 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
540 i->set_active (overlaid != 0 && stacked == 0);
541 i->set_inconsistent (overlaid != 0 && stacked != 0);
542 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
544 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
545 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
546 i->set_active (overlaid == 0 && stacked != 0);
547 i->set_inconsistent (overlaid != 0 && stacked != 0);
548 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
550 _ignore_set_layer_display = false;
552 items.push_back (MenuElem (_("Layers"), *layers_menu));
554 if (!Profile->get_sae()) {
556 Menu* alignment_menu = manage (new Menu);
557 MenuList& alignment_items = alignment_menu->items();
558 alignment_menu->set_name ("ArdourContextMenu");
560 RadioMenuItem::Group align_group;
562 /* Same verbose hacks as for the layering options above */
568 boost::shared_ptr<Track> first_track;
570 TrackSelection const & s = _editor.get_selection().tracks;
571 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
572 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
573 if (!r || !r->is_track ()) {
578 first_track = r->track();
581 switch (r->track()->alignment_choice()) {
585 switch (r->track()->alignment_style()) {
586 case ExistingMaterial:
594 case UseExistingMaterial:
610 inconsistent = false;
619 if (!inconsistent && first_track) {
621 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
622 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
623 i->set_active (automatic != 0 && existing == 0 && capture == 0);
624 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
626 switch (first_track->alignment_choice()) {
628 switch (first_track->alignment_style()) {
629 case ExistingMaterial:
630 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
633 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
641 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
642 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
643 i->set_active (existing != 0 && capture == 0 && automatic == 0);
644 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
646 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
647 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
648 i->set_active (existing == 0 && capture != 0 && automatic == 0);
649 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
651 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
657 Menu* mode_menu = manage (new Menu);
658 MenuList& mode_items = mode_menu->items ();
659 mode_menu->set_name ("ArdourContextMenu");
661 RadioMenuItem::Group mode_group;
667 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
668 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
669 if (!r || !r->is_track ()) {
673 switch (r->track()->mode()) {
686 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
687 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
688 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
689 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
690 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
692 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
693 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
694 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
695 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
696 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
698 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
699 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
701 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
702 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
704 items.push_back (MenuElem (_("Mode"), *mode_menu));
708 items.push_back (SeparatorElem());
710 build_playlist_menu ();
711 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
712 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
715 route_group_menu->detach ();
718 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
719 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
721 r.push_back (rtv->route ());
726 r.push_back (route ());
729 route_group_menu->build (r);
730 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
732 build_automation_action_menu (true);
733 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
735 items.push_back (SeparatorElem());
739 TrackSelection const & s = _editor.get_selection().tracks;
740 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
741 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
746 if (r->route()->active()) {
753 items.push_back (CheckMenuElem (_("Active")));
754 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
755 bool click_sets_active = true;
756 if (active > 0 && inactive == 0) {
757 i->set_active (true);
758 click_sets_active = false;
759 } else if (active > 0 && inactive > 0) {
760 i->set_inconsistent (true);
762 i->set_sensitive(! _session->transport_rolling());
763 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
765 items.push_back (SeparatorElem());
766 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
767 if (!Profile->get_sae()) {
768 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
770 items.push_front (SeparatorElem());
771 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
776 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
778 if (apply_to_selection) {
779 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
782 bool needs_bounce = false;
784 if (!track()->can_use_mode (mode, needs_bounce)) {
790 cerr << "would bounce this one\n";
795 track()->set_mode (mode);
797 rec_enable_button->remove ();
800 case ARDOUR::NonLayered:
802 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
803 rec_enable_button->set_text (string());
805 case ARDOUR::Destructive:
806 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
807 rec_enable_button->set_text (string());
811 rec_enable_button->show_all ();
816 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
818 TimeAxisView::show_timestretch (start, end, layers, layer);
828 /* check that the time selection was made in our route, or our route group.
829 remember that route_group() == 0 implies the route is *not* in a edit group.
832 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
833 /* this doesn't apply to us */
837 /* ignore it if our edit group is not active */
839 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
844 if (timestretch_rect == 0) {
845 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
846 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
847 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
850 timestretch_rect->show ();
851 timestretch_rect->raise_to_top ();
853 double const x1 = start / _editor.get_current_zoom();
854 double const x2 = (end - 1) / _editor.get_current_zoom();
856 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
857 x2, current_height() * (layers - layer) / layers));
861 RouteTimeAxisView::hide_timestretch ()
863 TimeAxisView::hide_timestretch ();
865 if (timestretch_rect) {
866 timestretch_rect->hide ();
871 RouteTimeAxisView::show_selection (TimeSelection& ts)
875 /* ignore it if our edit group is not active or if the selection was started
876 in some other track or route group (remember that route_group() == 0 means
877 that the track is not in an route group).
880 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
881 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
887 TimeAxisView::show_selection (ts);
891 RouteTimeAxisView::set_height (uint32_t h)
894 bool height_changed = (height == 0) || (h != height);
897 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
900 gm.get_level_meter().setup_meters (gmlen, meter_width);
902 TimeAxisView::set_height (h);
905 _view->set_height ((double) current_height());
908 if (height >= preset_height (HeightNormal)) {
912 gm.get_gain_slider().show();
914 if (!_route || _route->is_monitor()) {
919 if (rec_enable_button)
920 rec_enable_button->show();
922 route_group_button.show();
923 automation_button.show();
925 if (is_track() && track()->mode() == ARDOUR::Normal) {
926 playlist_button.show();
933 gm.get_gain_slider().hide();
935 if (!_route || _route->is_monitor()) {
940 if (rec_enable_button)
941 rec_enable_button->show();
943 route_group_button.hide ();
944 automation_button.hide ();
946 if (is_track() && track()->mode() == ARDOUR::Normal) {
947 playlist_button.hide ();
952 if (height_changed && !no_redraw) {
953 /* only emit the signal if the height really changed */
959 RouteTimeAxisView::route_color_changed ()
962 _view->apply_color (color(), StreamView::RegionColor);
967 RouteTimeAxisView::reset_samples_per_pixel ()
969 set_samples_per_pixel (_editor.get_current_zoom());
973 RouteTimeAxisView::set_samples_per_pixel (double fpp)
978 speed = track()->speed();
982 _view->set_samples_per_pixel (fpp * speed);
985 TimeAxisView::set_samples_per_pixel (fpp * speed);
989 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
991 if (!mitem->get_active()) {
992 /* this is one of the two calls made when these radio menu items change status. this one
993 is for the item that became inactive, and we want to ignore it.
998 if (apply_to_selection) {
999 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
1002 track()->set_align_choice (choice);
1008 RouteTimeAxisView::rename_current_playlist ()
1010 ArdourPrompter prompter (true);
1013 boost::shared_ptr<Track> tr = track();
1014 if (!tr || tr->destructive()) {
1018 boost::shared_ptr<Playlist> pl = tr->playlist();
1023 prompter.set_title (_("Rename Playlist"));
1024 prompter.set_prompt (_("New name for playlist:"));
1025 prompter.set_initial_text (pl->name());
1026 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1027 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1029 switch (prompter.run ()) {
1030 case Gtk::RESPONSE_ACCEPT:
1031 prompter.get_result (name);
1032 if (name.length()) {
1033 pl->set_name (name);
1043 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1045 std::string ret (basename);
1047 std::string const group_string = "." + route_group()->name() + ".";
1049 // iterate through all playlists
1051 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1052 std::string tmp = (*i)->name();
1054 std::string::size_type idx = tmp.find(group_string);
1055 // find those which belong to this group
1056 if (idx != string::npos) {
1057 tmp = tmp.substr(idx + group_string.length());
1059 // and find the largest current number
1061 if (x > maxnumber) {
1070 snprintf (buf, sizeof(buf), "%d", maxnumber);
1072 ret = this->name() + "." + route_group()->name () + "." + buf;
1078 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1082 boost::shared_ptr<Track> tr = track ();
1083 if (!tr || tr->destructive()) {
1087 boost::shared_ptr<const Playlist> pl = tr->playlist();
1094 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1095 name = resolve_new_group_playlist_name(name, playlists_before_op);
1098 while (_session->playlists->by_name(name)) {
1099 name = Playlist::bump_name (name, *_session);
1102 // TODO: The prompter "new" button should be de-activated if the user
1103 // specifies a playlist name which already exists in the session.
1107 ArdourPrompter prompter (true);
1109 prompter.set_title (_("New Copy Playlist"));
1110 prompter.set_prompt (_("Name for new playlist:"));
1111 prompter.set_initial_text (name);
1112 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1113 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1114 prompter.show_all ();
1116 switch (prompter.run ()) {
1117 case Gtk::RESPONSE_ACCEPT:
1118 prompter.get_result (name);
1126 if (name.length()) {
1127 tr->use_copy_playlist ();
1128 tr->playlist()->set_name (name);
1133 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1137 boost::shared_ptr<Track> tr = track ();
1138 if (!tr || tr->destructive()) {
1142 boost::shared_ptr<const Playlist> pl = tr->playlist();
1149 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1150 name = resolve_new_group_playlist_name(name,playlists_before_op);
1153 while (_session->playlists->by_name(name)) {
1154 name = Playlist::bump_name (name, *_session);
1160 ArdourPrompter prompter (true);
1162 prompter.set_title (_("New Playlist"));
1163 prompter.set_prompt (_("Name for new playlist:"));
1164 prompter.set_initial_text (name);
1165 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1166 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1168 switch (prompter.run ()) {
1169 case Gtk::RESPONSE_ACCEPT:
1170 prompter.get_result (name);
1178 if (name.length()) {
1179 tr->use_new_playlist ();
1180 tr->playlist()->set_name (name);
1185 RouteTimeAxisView::clear_playlist ()
1187 boost::shared_ptr<Track> tr = track ();
1188 if (!tr || tr->destructive()) {
1192 boost::shared_ptr<Playlist> pl = tr->playlist();
1197 _editor.clear_playlist (pl);
1201 RouteTimeAxisView::speed_changed ()
1203 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1207 RouteTimeAxisView::update_diskstream_display ()
1217 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1219 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1221 /* special case: select/deselect all tracks */
1222 if (_editor.get_selection().selected (this)) {
1223 _editor.get_selection().clear_tracks ();
1225 _editor.select_all_tracks ();
1231 switch (ArdourKeyboard::selection_type (ev->state)) {
1232 case Selection::Toggle:
1233 _editor.get_selection().toggle (this);
1236 case Selection::Set:
1237 _editor.get_selection().set (this);
1240 case Selection::Extend:
1241 _editor.extend_selection_to_track (*this);
1244 case Selection::Add:
1245 _editor.get_selection().add (this);
1251 RouteTimeAxisView::set_selected_points (PointSelection& points)
1253 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1254 (*i)->set_selected_points (points);
1259 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1262 _view->set_selected_regionviews (regions);
1266 /** Add the selectable things that we have to a list.
1267 * @param results List to add things to.
1270 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1275 speed = track()->speed();
1278 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1279 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1281 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1282 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1285 /* pick up visible automation tracks */
1287 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1288 if (!(*i)->hidden()) {
1289 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1295 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1298 _view->get_inverted_selectables (sel, results);
1301 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1302 if (!(*i)->hidden()) {
1303 (*i)->get_inverted_selectables (sel, results);
1311 RouteTimeAxisView::route_group () const
1313 return _route->route_group();
1317 RouteTimeAxisView::name() const
1319 return _route->name();
1322 boost::shared_ptr<Playlist>
1323 RouteTimeAxisView::playlist () const
1325 boost::shared_ptr<Track> tr;
1327 if ((tr = track()) != 0) {
1328 return tr->playlist();
1330 return boost::shared_ptr<Playlist> ();
1335 RouteTimeAxisView::name_entry_changed ()
1337 TimeAxisView::name_entry_changed ();
1339 string x = name_entry->get_text ();
1341 if (x == _route->name()) {
1345 strip_whitespace_edges (x);
1347 if (x.length() == 0) {
1348 name_entry->set_text (_route->name());
1352 if (_session->route_name_internal (x)) {
1353 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1355 name_entry->grab_focus ();
1356 } else if (RouteUI::verify_new_route_name (x)) {
1357 _route->set_name (x);
1359 name_entry->grab_focus ();
1363 boost::shared_ptr<Region>
1364 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1366 boost::shared_ptr<Playlist> pl = playlist ();
1369 return pl->find_next_region (pos, point, dir);
1372 return boost::shared_ptr<Region> ();
1376 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1378 boost::shared_ptr<Playlist> pl = playlist ();
1381 return pl->find_next_region_boundary (pos, dir);
1388 RouteTimeAxisView::fade_range (TimeSelection& selection)
1390 boost::shared_ptr<Playlist> what_we_got;
1391 boost::shared_ptr<Track> tr = track ();
1392 boost::shared_ptr<Playlist> playlist;
1395 /* route is a bus, not a track */
1399 playlist = tr->playlist();
1401 TimeSelection time (selection);
1402 float const speed = tr->speed();
1403 if (speed != 1.0f) {
1404 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1405 (*i).start = session_frame_to_track_frame((*i).start, speed);
1406 (*i).end = session_frame_to_track_frame((*i).end, speed);
1410 playlist->clear_changes ();
1411 playlist->clear_owned_changes ();
1413 playlist->fade_range (time);
1415 vector<Command*> cmds;
1416 playlist->rdiff (cmds);
1417 _session->add_commands (cmds);
1418 _session->add_command (new StatefulDiffCommand (playlist));
1423 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1425 boost::shared_ptr<Playlist> what_we_got;
1426 boost::shared_ptr<Track> tr = track ();
1427 boost::shared_ptr<Playlist> playlist;
1430 /* route is a bus, not a track */
1434 playlist = tr->playlist();
1436 TimeSelection time (selection.time);
1437 float const speed = tr->speed();
1438 if (speed != 1.0f) {
1439 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1440 (*i).start = session_frame_to_track_frame((*i).start, speed);
1441 (*i).end = session_frame_to_track_frame((*i).end, speed);
1445 playlist->clear_changes ();
1446 playlist->clear_owned_changes ();
1450 if (playlist->cut (time) != 0) {
1451 if (Config->get_edit_mode() == Ripple)
1452 playlist->ripple(time.start(), -time.length(), NULL);
1453 // no need to exclude any regions from rippling here
1455 vector<Command*> cmds;
1456 playlist->rdiff (cmds);
1457 _session->add_commands (cmds);
1459 _session->add_command (new StatefulDiffCommand (playlist));
1464 if ((what_we_got = playlist->cut (time)) != 0) {
1465 _editor.get_cut_buffer().add (what_we_got);
1466 if (Config->get_edit_mode() == Ripple)
1467 playlist->ripple(time.start(), -time.length(), NULL);
1468 // no need to exclude any regions from rippling here
1470 vector<Command*> cmds;
1471 playlist->rdiff (cmds);
1472 _session->add_commands (cmds);
1474 _session->add_command (new StatefulDiffCommand (playlist));
1478 if ((what_we_got = playlist->copy (time)) != 0) {
1479 _editor.get_cut_buffer().add (what_we_got);
1484 if ((what_we_got = playlist->cut (time)) != 0) {
1485 if (Config->get_edit_mode() == Ripple)
1486 playlist->ripple(time.start(), -time.length(), NULL);
1487 // no need to exclude any regions from rippling here
1489 vector<Command*> cmds;
1490 playlist->rdiff (cmds);
1491 _session->add_commands (cmds);
1492 _session->add_command (new StatefulDiffCommand (playlist));
1493 what_we_got->release ();
1500 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1506 boost::shared_ptr<Playlist> pl = playlist ();
1507 PlaylistSelection::iterator p;
1509 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1511 if (p == selection.playlists.end()) {
1515 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1517 if (track()->speed() != 1.0f) {
1518 pos = session_frame_to_track_frame (pos, track()->speed());
1519 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1522 pl->clear_changes ();
1523 if (Config->get_edit_mode() == Ripple) {
1524 std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
1525 framecnt_t amount = extent.second - extent.first;
1526 pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
1528 pl->paste (*p, pos, times);
1530 vector<Command*> cmds;
1532 _session->add_commands (cmds);
1534 _session->add_command (new StatefulDiffCommand (pl));
1540 struct PlaylistSorter {
1541 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1542 return a->sort_id() < b->sort_id();
1547 RouteTimeAxisView::build_playlist_menu ()
1549 using namespace Menu_Helpers;
1555 delete playlist_action_menu;
1556 playlist_action_menu = new Menu;
1557 playlist_action_menu->set_name ("ArdourContextMenu");
1559 MenuList& playlist_items = playlist_action_menu->items();
1560 playlist_action_menu->set_name ("ArdourContextMenu");
1561 playlist_items.clear();
1563 RadioMenuItem::Group playlist_group;
1564 boost::shared_ptr<Track> tr = track ();
1566 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1568 /* sort the playlists */
1570 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1572 /* add the playlists to the menu */
1573 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1574 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1575 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1576 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1578 if (tr->playlist()->id() == (*i)->id()) {
1584 playlist_items.push_back (SeparatorElem());
1585 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1586 playlist_items.push_back (SeparatorElem());
1588 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1589 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1590 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1593 // Use a label which tells the user what is happening
1594 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1595 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1599 playlist_items.push_back (SeparatorElem());
1600 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1601 playlist_items.push_back (SeparatorElem());
1603 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1607 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1609 assert (is_track());
1611 // exit if we were triggered by deactivating the old playlist
1612 if (!item->get_active()) {
1616 boost::shared_ptr<Playlist> pl (wpl.lock());
1622 if (track()->playlist() == pl) {
1623 // exit when use_playlist is called by the creation of the playlist menu
1624 // or the playlist choice is unchanged
1628 track()->use_playlist (pl);
1630 RouteGroup* rg = route_group();
1632 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1633 std::string group_string = "." + rg->name() + ".";
1635 std::string take_name = pl->name();
1636 std::string::size_type idx = take_name.find(group_string);
1638 if (idx == std::string::npos)
1641 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1643 boost::shared_ptr<RouteList> rl (rg->route_list());
1645 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1646 if ((*i) == this->route()) {
1650 std::string playlist_name = (*i)->name()+group_string+take_name;
1652 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1657 if (track->freeze_state() == Track::Frozen) {
1658 /* Don't change playlists of frozen tracks */
1662 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1664 // No playlist for this track for this take yet, make it
1665 track->use_new_playlist();
1666 track->playlist()->set_name(playlist_name);
1668 track->use_playlist(ipl);
1675 RouteTimeAxisView::update_playlist_tip ()
1677 RouteGroup* rg = route_group ();
1678 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1679 string group_string = "." + rg->name() + ".";
1681 string take_name = track()->playlist()->name();
1682 string::size_type idx = take_name.find(group_string);
1684 if (idx != string::npos) {
1685 /* find the bit containing the take number / name */
1686 take_name = take_name.substr (idx + group_string.length());
1688 /* set the playlist button tooltip to the take name */
1689 ARDOUR_UI::instance()->set_tip (
1691 string_compose(_("Take: %1.%2"),
1692 Glib::Markup::escape_text(rg->name()),
1693 Glib::Markup::escape_text(take_name))
1700 /* set the playlist button tooltip to the playlist name */
1701 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1706 RouteTimeAxisView::show_playlist_selector ()
1708 _editor.playlist_selector().show_for (this);
1712 RouteTimeAxisView::map_frozen ()
1718 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1720 switch (track()->freeze_state()) {
1722 playlist_button.set_sensitive (false);
1723 rec_enable_button->set_sensitive (false);
1726 playlist_button.set_sensitive (true);
1727 rec_enable_button->set_sensitive (true);
1733 RouteTimeAxisView::color_handler ()
1735 //case cTimeStretchOutline:
1736 if (timestretch_rect) {
1737 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1739 //case cTimeStretchFill:
1740 if (timestretch_rect) {
1741 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1747 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1748 * Will add track if necessary.
1751 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1753 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1754 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1757 /* it doesn't exist yet, so we don't care about the button state: just add it */
1758 create_automation_child (param, true);
1761 bool yn = menu->get_active();
1762 bool changed = false;
1764 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1766 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1767 will have done that for us.
1770 if (changed && !no_redraw) {
1778 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1780 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1786 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1788 if (menu && !_hidden) {
1789 ignore_toggle = true;
1790 menu->set_active (false);
1791 ignore_toggle = false;
1794 if (_route && !no_redraw) {
1801 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1803 if (apply_to_selection) {
1804 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1808 /* Show our automation */
1810 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1811 i->second->set_marked_for_display (true);
1813 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1816 menu->set_active(true);
1821 /* Show processor automation */
1823 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1824 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1825 if ((*ii)->view == 0) {
1826 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1829 (*ii)->menu_item->set_active (true);
1842 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1844 if (apply_to_selection) {
1845 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1849 /* Show our automation */
1851 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1852 if (i->second->has_automation()) {
1853 i->second->set_marked_for_display (true);
1855 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1857 menu->set_active(true);
1862 /* Show processor automation */
1864 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1865 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1866 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1867 (*ii)->menu_item->set_active (true);
1879 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1881 if (apply_to_selection) {
1882 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1886 /* Hide our automation */
1888 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1889 i->second->set_marked_for_display (false);
1891 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1894 menu->set_active (false);
1898 /* Hide processor automation */
1900 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1901 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1902 (*ii)->menu_item->set_active (false);
1913 RouteTimeAxisView::region_view_added (RegionView* rv)
1915 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1916 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1917 boost::shared_ptr<AutomationTimeAxisView> atv;
1919 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1924 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1925 (*i)->add_ghost(rv);
1929 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1931 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1937 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1939 parent.remove_processor_automation_node (this);
1943 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1946 remove_child (pan->view);
1950 RouteTimeAxisView::ProcessorAutomationNode*
1951 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1953 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1955 if ((*i)->processor == processor) {
1957 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1958 if ((*ii)->what == what) {
1968 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1970 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1973 ProcessorAutomationNode* pan;
1975 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1976 /* session state may never have been saved with new plugin */
1977 error << _("programming error: ")
1978 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1979 processor->name(), what.type(), (int) what.channel(), what.id() )
1989 boost::shared_ptr<AutomationControl> control
1990 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1992 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1993 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1994 _editor, *this, false, parent_canvas,
1995 processor->describe_parameter (what), processor->name()));
1997 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1999 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
2002 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
2007 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
2010 pan->menu_item->set_active (false);
2019 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
2021 boost::shared_ptr<Processor> processor (p.lock ());
2023 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
2024 /* The Amp processor is a special case and is dealt with separately */
2028 set<Evoral::Parameter> existing;
2030 processor->what_has_data (existing);
2032 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
2034 Evoral::Parameter param (*i);
2035 boost::shared_ptr<AutomationLine> al;
2037 if ((al = find_processor_automation_curve (processor, param)) != 0) {
2040 add_processor_automation_curve (processor, param);
2046 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
2048 using namespace Menu_Helpers;
2052 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2054 _automation_tracks[param] = track;
2056 /* existing state overrides "show" argument */
2057 string s = track->gui_property ("visible");
2059 show = string_is_affirmative (s);
2062 /* this might or might not change the visibility status, so don't rely on it */
2063 track->set_marked_for_display (show);
2065 if (show && !no_redraw) {
2069 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2070 /* MIDI-related parameters are always in the menu, there's no
2071 reason to rebuild the menu just because we added a automation
2072 lane for one of them. But if we add a non-MIDI automation
2073 lane, then we need to invalidate the display menu.
2075 delete display_menu;
2081 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2083 boost::shared_ptr<Processor> processor (p.lock ());
2085 if (!processor || !processor->display_to_user ()) {
2089 /* we use this override to veto the Amp processor from the plugin menu,
2090 as its automation lane can be accessed using the special "Fader" menu
2094 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
2098 using namespace Menu_Helpers;
2099 ProcessorAutomationInfo *rai;
2100 list<ProcessorAutomationInfo*>::iterator x;
2102 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2104 if (automatable.empty()) {
2108 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2109 if ((*x)->processor == processor) {
2114 if (x == processor_automation.end()) {
2116 rai = new ProcessorAutomationInfo (processor);
2117 processor_automation.push_back (rai);
2125 /* any older menu was deleted at the top of processors_changed()
2126 when we cleared the subplugin menu.
2129 rai->menu = manage (new Menu);
2130 MenuList& items = rai->menu->items();
2131 rai->menu->set_name ("ArdourContextMenu");
2135 std::set<Evoral::Parameter> has_visible_automation;
2136 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2138 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2140 ProcessorAutomationNode* pan;
2141 Gtk::CheckMenuItem* mitem;
2143 string name = processor->describe_parameter (*i);
2145 items.push_back (CheckMenuElem (name));
2146 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2148 _subplugin_menu_map[*i] = mitem;
2150 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2151 mitem->set_active(true);
2154 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2158 pan = new ProcessorAutomationNode (*i, mitem, *this);
2160 rai->lines.push_back (pan);
2164 pan->menu_item = mitem;
2168 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2171 /* add the menu for this processor, because the subplugin
2172 menu is always cleared at the top of processors_changed().
2173 this is the result of some poor design in gtkmm and/or
2177 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2182 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2183 RouteTimeAxisView::ProcessorAutomationNode* pan)
2185 bool showit = pan->menu_item->get_active();
2186 bool redraw = false;
2188 if (pan->view == 0 && showit) {
2189 add_processor_automation_curve (rai->processor, pan->what);
2193 if (pan->view && pan->view->set_marked_for_display (showit)) {
2197 if (redraw && !no_redraw) {
2203 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2205 if (c.type == RouteProcessorChange::MeterPointChange) {
2206 /* nothing to do if only the meter point has changed */
2210 using namespace Menu_Helpers;
2212 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2213 (*i)->valid = false;
2216 setup_processor_menu_and_curves ();
2218 bool deleted_processor_automation = false;
2220 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2222 list<ProcessorAutomationInfo*>::iterator tmp;
2230 processor_automation.erase (i);
2231 deleted_processor_automation = true;
2238 if (deleted_processor_automation && !no_redraw) {
2243 boost::shared_ptr<AutomationLine>
2244 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2246 ProcessorAutomationNode* pan;
2248 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2254 return boost::shared_ptr<AutomationLine>();
2258 RouteTimeAxisView::reset_processor_automation_curves ()
2260 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2266 RouteTimeAxisView::can_edit_name () const
2268 /* we do not allow track name changes if it is record enabled
2270 return !_route->record_enabled();
2274 RouteTimeAxisView::update_rec_display ()
2276 RouteUI::update_rec_display ();
2280 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2282 if (_ignore_set_layer_display) {
2286 if (apply_to_selection) {
2287 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2291 _view->set_layer_display (d);
2294 set_gui_property (X_("layer-display"), enum_2_string (d));
2299 RouteTimeAxisView::layer_display () const
2302 return _view->layer_display ();
2305 /* we don't know, since we don't have a _view, so just return something */
2311 boost::shared_ptr<AutomationTimeAxisView>
2312 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2314 AutomationTracks::iterator i = _automation_tracks.find(param);
2315 if (i != _automation_tracks.end()) {
2318 return boost::shared_ptr<AutomationTimeAxisView>();
2323 RouteTimeAxisView::fast_update ()
2325 gm.get_level_meter().update_meters ();
2329 RouteTimeAxisView::hide_meter ()
2332 gm.get_level_meter().hide_meters ();
2336 RouteTimeAxisView::show_meter ()
2342 RouteTimeAxisView::reset_meter ()
2344 if (Config->get_show_track_meters()) {
2345 int meter_width = 3;
2346 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2349 gm.get_level_meter().setup_meters (height - 9, meter_width);
2356 RouteTimeAxisView::clear_meter ()
2358 gm.get_level_meter().clear_meters ();
2362 RouteTimeAxisView::meter_changed ()
2364 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2366 if (_route && !no_redraw) {
2369 // reset peak when meter point changes
2370 gm.reset_peak_display();
2374 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2377 if (_route && !no_redraw) {
2383 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2385 using namespace Menu_Helpers;
2387 if (!_underlay_streams.empty()) {
2388 MenuList& parent_items = parent_menu->items();
2389 Menu* gs_menu = manage (new Menu);
2390 gs_menu->set_name ("ArdourContextMenu");
2391 MenuList& gs_items = gs_menu->items();
2393 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2395 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2396 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2397 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2403 RouteTimeAxisView::set_underlay_state()
2405 if (!underlay_xml_node) {
2409 XMLNodeList nlist = underlay_xml_node->children();
2410 XMLNodeConstIterator niter;
2411 XMLNode *child_node;
2413 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2414 child_node = *niter;
2416 if (child_node->name() != "Underlay") {
2420 XMLProperty* prop = child_node->property ("id");
2422 PBD::ID id (prop->value());
2424 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2427 add_underlay(v->view(), false);
2436 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2442 RouteTimeAxisView& other = v->trackview();
2444 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2445 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2446 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2450 _underlay_streams.push_back(v);
2451 other._underlay_mirrors.push_back(this);
2453 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2455 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2457 if (!underlay_xml_node) {
2458 underlay_xml_node = xml_node->add_child("Underlays");
2461 XMLNode* node = underlay_xml_node->add_child("Underlay");
2462 XMLProperty* prop = node->add_property("id");
2463 prop->set_value(v->trackview().route()->id().to_s());
2470 RouteTimeAxisView::remove_underlay (StreamView* v)
2476 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2477 RouteTimeAxisView& other = v->trackview();
2479 if (it != _underlay_streams.end()) {
2480 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2482 if (gm == other._underlay_mirrors.end()) {
2483 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2487 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2489 _underlay_streams.erase(it);
2490 other._underlay_mirrors.erase(gm);
2492 if (underlay_xml_node) {
2493 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2499 RouteTimeAxisView::set_button_names ()
2501 if (_route && _route->solo_safe()) {
2502 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2504 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2506 if (Config->get_solo_control_is_listen_control()) {
2507 switch (Config->get_listen_position()) {
2508 case AfterFaderListen:
2509 solo_button->set_text (_("A"));
2510 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2512 case PreFaderListen:
2513 solo_button->set_text (_("P"));
2514 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2518 solo_button->set_text (_("s"));
2519 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2521 mute_button->set_text (_("m"));
2525 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2527 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2528 if (i != _main_automation_menu_map.end()) {
2532 i = _subplugin_menu_map.find (param);
2533 if (i != _subplugin_menu_map.end()) {
2541 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2543 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2545 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2549 gain_track.reset (new AutomationTimeAxisView (_session,
2550 _route, _route->amp(), c, param,
2555 _route->amp()->describe_parameter(param)));
2558 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2561 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2565 RouteTimeAxisView::create_mute_automation_child (const Evoral::Parameter& param, bool show)
2567 boost::shared_ptr<AutomationControl> c = _route->mute_control();
2569 error << "Route has no mute automation, unable to add automation track view." << endmsg;
2573 mute_track.reset (new AutomationTimeAxisView (_session,
2574 _route, _route, c, param,
2579 _route->describe_parameter(param)));
2582 _view->foreach_regionview (sigc::mem_fun (*mute_track.get(), &TimeAxisView::add_ghost));
2585 add_automation_child (Evoral::Parameter(MuteAutomation), mute_track, show);
2589 void add_region_to_list (RegionView* rv, RegionList* l)
2591 l->push_back (rv->region());
2595 RouteTimeAxisView::combine_regions ()
2597 /* as of may 2011, we do not offer uncombine for MIDI tracks
2600 if (!is_audio_track()) {
2608 RegionList selected_regions;
2609 boost::shared_ptr<Playlist> playlist = track()->playlist();
2611 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2613 if (selected_regions.size() < 2) {
2617 playlist->clear_changes ();
2618 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2620 _session->add_command (new StatefulDiffCommand (playlist));
2621 /* make the new region be selected */
2623 return _view->find_view (compound_region);
2627 RouteTimeAxisView::uncombine_regions ()
2629 /* as of may 2011, we do not offer uncombine for MIDI tracks
2631 if (!is_audio_track()) {
2639 RegionList selected_regions;
2640 boost::shared_ptr<Playlist> playlist = track()->playlist();
2642 /* have to grab selected regions first because the uncombine is going
2643 * to change that in the middle of the list traverse
2646 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2648 playlist->clear_changes ();
2650 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2651 playlist->uncombine (*i);
2654 _session->add_command (new StatefulDiffCommand (playlist));
2658 RouteTimeAxisView::state_id() const
2660 return string_compose ("rtav %1", _route->id().to_s());
2665 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2667 TimeAxisView::remove_child (c);
2669 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2671 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2672 if (i->second == a) {
2673 _automation_tracks.erase (i);