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)
114 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
116 RouteUI::set_route (rt);
118 CANVAS_DEBUG_NAME (_canvas_display, string_compose ("main for %1", rt->name()));
119 CANVAS_DEBUG_NAME (selection_group, string_compose ("selections for %1", rt->name()));
120 CANVAS_DEBUG_NAME (_ghost_group, string_compose ("ghosts for %1", rt->name()));
123 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
126 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
127 gm.get_level_meter().set_no_show_all();
128 gm.get_level_meter().setup_meters(50, meter_width);
129 gm.update_gain_sensitive ();
131 string str = gui_property ("height");
133 set_height (atoi (str));
135 set_height (preset_height (HeightNormal));
138 if (!_route->is_auditioner()) {
139 if (gui_property ("visible").empty()) {
140 set_gui_property ("visible", true);
143 set_gui_property ("visible", false);
147 update_solo_display ();
149 timestretch_rect = 0;
152 ignore_toggle = false;
154 route_group_button.set_name ("route button");
155 playlist_button.set_name ("route button");
156 automation_button.set_name ("route button");
158 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
159 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
160 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
166 switch (track()->mode()) {
168 case ARDOUR::NonLayered:
169 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
171 case ARDOUR::Destructive:
172 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
176 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
178 if (is_midi_track()) {
179 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
180 gm.set_fader_name ("MidiTrackFader");
182 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
183 gm.set_fader_name ("AudioTrackFader");
186 rec_enable_button->set_sensitive (_session->writable());
188 /* set playlist button tip to the current playlist, and make it update when it changes */
189 update_playlist_tip ();
190 track()->PlaylistChanged.connect (*this, invalidator (*this), ui_bind(&RouteTimeAxisView::update_playlist_tip, this), gui_context());
193 gm.set_fader_name ("AudioBusFader");
196 Gtk::VBox *mtrbox = manage(new Gtk::VBox());
197 mtrbox->pack_start(gm.get_level_meter(), false, false, 2);
198 controls_hbox.pack_start(*mtrbox, false, false, 4);
201 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
202 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
203 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
205 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
207 if (!_route->is_master()) {
208 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
211 if (!ARDOUR::Profile->get_trx()) {
212 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
213 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::AttachOptions (0), 3, 0);
216 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
217 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
218 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
220 if (is_midi_track()) {
221 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
223 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
228 if (!ARDOUR::Profile->get_trx()) {
229 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
232 if (!ARDOUR::Profile->get_trx() && is_track() && track()->mode() == ARDOUR::Normal) {
233 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
238 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
239 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
243 str = gui_property ("layer-display");
245 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
248 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
249 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
251 /* pick up the correct freeze state */
256 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_pixel));
257 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
259 PropertyList* plist = new PropertyList();
261 plist->add (ARDOUR::Properties::mute, true);
262 plist->add (ARDOUR::Properties::solo, true);
264 route_group_menu = new RouteGroupMenu (_session, plist);
266 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
268 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
271 RouteTimeAxisView::~RouteTimeAxisView ()
273 CatchDeletion (this);
275 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
279 delete playlist_action_menu;
280 playlist_action_menu = 0;
285 _automation_tracks.clear ();
287 delete route_group_menu;
291 RouteTimeAxisView::post_construct ()
293 /* map current state of the route */
295 update_diskstream_display ();
296 setup_processor_menu_and_curves ();
297 reset_processor_automation_curves ();
300 /** Set up the processor menu for the current set of processors, and
301 * display automation curves for any parameters which have data.
304 RouteTimeAxisView::setup_processor_menu_and_curves ()
306 _subplugin_menu_map.clear ();
307 subplugin_menu.items().clear ();
308 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
309 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
313 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
315 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
316 if (_route->route_group()) {
317 _route->route_group()->remove (_route);
323 r.push_back (route ());
325 route_group_menu->build (r);
326 route_group_menu->menu()->popup (ev->button, ev->time);
332 RouteTimeAxisView::playlist_changed ()
338 RouteTimeAxisView::label_view ()
340 string x = _route->name();
342 if (x != name_label.get_text()) {
343 name_label.set_text (x);
349 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
351 if (what_changed.contains (ARDOUR::Properties::name)) {
357 RouteTimeAxisView::take_name_changed (void *src)
365 RouteTimeAxisView::playlist_click ()
367 build_playlist_menu ();
368 conditionally_add_to_selection ();
369 playlist_action_menu->popup (1, gtk_get_current_event_time());
373 RouteTimeAxisView::automation_click ()
375 conditionally_add_to_selection ();
376 build_automation_action_menu (false);
377 automation_action_menu->popup (1, gtk_get_current_event_time());
381 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
383 using namespace Menu_Helpers;
385 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
386 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
389 detach_menu (subplugin_menu);
391 _main_automation_menu_map.clear ();
392 delete automation_action_menu;
393 automation_action_menu = new Menu;
395 MenuList& items = automation_action_menu->items();
397 automation_action_menu->set_name ("ArdourContextMenu");
399 items.push_back (MenuElem (_("Show All Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
402 items.push_back (MenuElem (_("Show Existing Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
405 items.push_back (MenuElem (_("Hide All Automation"),
406 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
408 /* Attach the plugin submenu. It may have previously been used elsewhere,
409 so it was detached above
412 if (!subplugin_menu.items().empty()) {
413 items.push_back (SeparatorElem ());
414 items.push_back (MenuElem (_("Processor automation"), subplugin_menu));
415 items.back().set_sensitive (!for_selection || _editor.get_selection().tracks.size() == 1);;
420 RouteTimeAxisView::build_display_menu ()
422 using namespace Menu_Helpers;
426 TimeAxisView::build_display_menu ();
428 /* now fill it with our stuff */
430 MenuList& items = display_menu->items();
431 display_menu->set_name ("ArdourContextMenu");
433 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
436 detach_menu (*_size_menu);
439 items.push_back (MenuElem (_("Height"), *_size_menu));
441 items.push_back (SeparatorElem());
443 if (!Profile->get_sae()) {
444 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
445 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
446 items.push_back (SeparatorElem());
449 // Hook for derived classes to add type specific stuff
450 append_extra_display_menu_items ();
454 Menu* layers_menu = manage (new Menu);
455 MenuList &layers_items = layers_menu->items();
456 layers_menu->set_name("ArdourContextMenu");
458 RadioMenuItem::Group layers_group;
460 /* Find out how many overlaid/stacked tracks we have in the selection */
464 TrackSelection const & s = _editor.get_selection().tracks;
465 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
466 StreamView* v = (*i)->view ();
471 switch (v->layer_display ()) {
482 /* We're not connecting to signal_toggled() here; in the case where these two items are
483 set to be in the `inconsistent' state, it seems that one or other will end up active
484 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
485 select the active one, no toggled signal is emitted so nothing happens.
488 _ignore_set_layer_display = true;
490 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
491 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
492 i->set_active (overlaid != 0 && stacked == 0);
493 i->set_inconsistent (overlaid != 0 && stacked != 0);
494 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
496 layers_items.push_back (RadioMenuElem (layers_group, _("Stacked")));
497 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
498 i->set_active (overlaid == 0 && stacked != 0);
499 i->set_inconsistent (overlaid != 0 && stacked != 0);
500 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
502 _ignore_set_layer_display = false;
504 items.push_back (MenuElem (_("Layers"), *layers_menu));
506 if (!Profile->get_sae()) {
508 Menu* alignment_menu = manage (new Menu);
509 MenuList& alignment_items = alignment_menu->items();
510 alignment_menu->set_name ("ArdourContextMenu");
512 RadioMenuItem::Group align_group;
514 /* Same verbose hacks as for the layering options above */
520 boost::shared_ptr<Track> first_track;
522 TrackSelection const & s = _editor.get_selection().tracks;
523 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
524 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
525 if (!r || !r->is_track ()) {
530 first_track = r->track();
533 switch (r->track()->alignment_choice()) {
537 switch (r->track()->alignment_style()) {
538 case ExistingMaterial:
546 case UseExistingMaterial:
562 inconsistent = false;
571 if (!inconsistent && first_track) {
573 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
574 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
575 i->set_active (automatic != 0 && existing == 0 && capture == 0);
576 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
578 switch (first_track->alignment_choice()) {
580 switch (first_track->alignment_style()) {
581 case ExistingMaterial:
582 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
585 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing != 0 && capture == 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
598 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
599 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
600 i->set_active (existing == 0 && capture != 0 && automatic == 0);
601 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
603 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
609 Menu* mode_menu = manage (new Menu);
610 MenuList& mode_items = mode_menu->items ();
611 mode_menu->set_name ("ArdourContextMenu");
613 RadioMenuItem::Group mode_group;
619 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
620 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
621 if (!r || !r->is_track ()) {
625 switch (r->track()->mode()) {
638 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
641 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
642 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
644 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
645 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
647 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
648 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
650 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
651 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
652 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
653 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
654 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
656 items.push_back (MenuElem (_("Mode"), *mode_menu));
660 items.push_back (SeparatorElem());
662 build_playlist_menu ();
663 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
664 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
667 route_group_menu->detach ();
670 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
671 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
673 r.push_back (rtv->route ());
678 r.push_back (route ());
681 route_group_menu->build (r);
682 items.push_back (MenuElem (_("Group"), *route_group_menu->menu ()));
684 build_automation_action_menu (true);
685 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
687 items.push_back (SeparatorElem());
691 TrackSelection const & s = _editor.get_selection().tracks;
692 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
693 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
698 if (r->route()->active()) {
705 items.push_back (CheckMenuElem (_("Active")));
706 Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
707 bool click_sets_active = true;
708 if (active > 0 && inactive == 0) {
709 i->set_active (true);
710 click_sets_active = false;
711 } else if (active > 0 && inactive > 0) {
712 i->set_inconsistent (true);
714 i->set_sensitive(! _session->transport_rolling());
715 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
717 items.push_back (SeparatorElem());
718 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
719 if (!Profile->get_sae()) {
720 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
722 items.push_front (SeparatorElem());
723 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
728 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
730 if (apply_to_selection) {
731 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
734 bool needs_bounce = false;
736 if (!track()->can_use_mode (mode, needs_bounce)) {
742 cerr << "would bounce this one\n";
747 track()->set_mode (mode);
749 rec_enable_button->remove ();
752 case ARDOUR::NonLayered:
754 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
755 rec_enable_button->set_text (string());
757 case ARDOUR::Destructive:
758 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
759 rec_enable_button->set_text (string());
763 rec_enable_button->show_all ();
768 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end, int layers, int layer)
770 TimeAxisView::show_timestretch (start, end, layers, layer);
780 /* check that the time selection was made in our route, or our route group.
781 remember that route_group() == 0 implies the route is *not* in a edit group.
784 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
785 /* this doesn't apply to us */
789 /* ignore it if our edit group is not active */
791 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
796 if (timestretch_rect == 0) {
797 timestretch_rect = new ArdourCanvas::Rectangle (canvas_display ());
798 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
799 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
802 timestretch_rect->show ();
803 timestretch_rect->raise_to_top ();
805 double const x1 = start / _editor.get_current_zoom();
806 double const x2 = (end - 1) / _editor.get_current_zoom();
808 timestretch_rect->set (ArdourCanvas::Rect (x1, current_height() * (layers - layer - 1) / layers,
809 x2, current_height() * (layers - layer) / layers));
813 RouteTimeAxisView::hide_timestretch ()
815 TimeAxisView::hide_timestretch ();
817 if (timestretch_rect) {
818 timestretch_rect->hide ();
823 RouteTimeAxisView::show_selection (TimeSelection& ts)
827 /* ignore it if our edit group is not active or if the selection was started
828 in some other track or route group (remember that route_group() == 0 means
829 that the track is not in an route group).
832 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
833 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
839 TimeAxisView::show_selection (ts);
843 RouteTimeAxisView::set_height (uint32_t h)
846 bool height_changed = (height == 0) || (h != height);
849 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
852 gm.get_level_meter().setup_meters (gmlen, meter_width);
854 TimeAxisView::set_height (h);
857 _view->set_height ((double) current_height());
860 if (height >= preset_height (HeightNormal)) {
864 gm.get_gain_slider().show();
866 if (!_route || _route->is_monitor()) {
871 if (rec_enable_button)
872 rec_enable_button->show();
874 route_group_button.show();
875 automation_button.show();
877 if (is_track() && track()->mode() == ARDOUR::Normal) {
878 playlist_button.show();
885 gm.get_gain_slider().hide();
887 if (!_route || _route->is_monitor()) {
892 if (rec_enable_button)
893 rec_enable_button->show();
895 route_group_button.hide ();
896 automation_button.hide ();
898 if (is_track() && track()->mode() == ARDOUR::Normal) {
899 playlist_button.hide ();
904 if (height_changed && !no_redraw) {
905 /* only emit the signal if the height really changed */
911 RouteTimeAxisView::route_color_changed ()
914 _view->apply_color (color(), StreamView::RegionColor);
919 RouteTimeAxisView::reset_samples_per_pixel ()
921 set_samples_per_pixel (_editor.get_current_zoom());
925 RouteTimeAxisView::set_samples_per_pixel (double fpp)
930 speed = track()->speed();
934 _view->set_samples_per_pixel (fpp * speed);
937 TimeAxisView::set_samples_per_pixel (fpp * speed);
941 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
943 if (!mitem->get_active()) {
944 /* this is one of the two calls made when these radio menu items change status. this one
945 is for the item that became inactive, and we want to ignore it.
950 if (apply_to_selection) {
951 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
954 track()->set_align_choice (choice);
960 RouteTimeAxisView::rename_current_playlist ()
962 ArdourPrompter prompter (true);
965 boost::shared_ptr<Track> tr = track();
966 if (!tr || tr->destructive()) {
970 boost::shared_ptr<Playlist> pl = tr->playlist();
975 prompter.set_title (_("Rename Playlist"));
976 prompter.set_prompt (_("New name for playlist:"));
977 prompter.set_initial_text (pl->name());
978 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
979 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
981 switch (prompter.run ()) {
982 case Gtk::RESPONSE_ACCEPT:
983 prompter.get_result (name);
995 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
997 std::string ret (basename);
999 std::string const group_string = "." + route_group()->name() + ".";
1001 // iterate through all playlists
1003 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1004 std::string tmp = (*i)->name();
1006 std::string::size_type idx = tmp.find(group_string);
1007 // find those which belong to this group
1008 if (idx != string::npos) {
1009 tmp = tmp.substr(idx + group_string.length());
1011 // and find the largest current number
1013 if (x > maxnumber) {
1022 snprintf (buf, sizeof(buf), "%d", maxnumber);
1024 ret = this->name() + "." + route_group()->name () + "." + buf;
1030 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1034 boost::shared_ptr<Track> tr = track ();
1035 if (!tr || tr->destructive()) {
1039 boost::shared_ptr<const Playlist> pl = tr->playlist();
1046 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1047 name = resolve_new_group_playlist_name(name, playlists_before_op);
1050 while (_session->playlists->by_name(name)) {
1051 name = Playlist::bump_name (name, *_session);
1054 // TODO: The prompter "new" button should be de-activated if the user
1055 // specifies a playlist name which already exists in the session.
1059 ArdourPrompter prompter (true);
1061 prompter.set_title (_("New Copy Playlist"));
1062 prompter.set_prompt (_("Name for new playlist:"));
1063 prompter.set_initial_text (name);
1064 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1065 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1066 prompter.show_all ();
1068 switch (prompter.run ()) {
1069 case Gtk::RESPONSE_ACCEPT:
1070 prompter.get_result (name);
1078 if (name.length()) {
1079 tr->use_copy_playlist ();
1080 tr->playlist()->set_name (name);
1085 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1089 boost::shared_ptr<Track> tr = track ();
1090 if (!tr || tr->destructive()) {
1094 boost::shared_ptr<const Playlist> pl = tr->playlist();
1101 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1102 name = resolve_new_group_playlist_name(name,playlists_before_op);
1105 while (_session->playlists->by_name(name)) {
1106 name = Playlist::bump_name (name, *_session);
1112 ArdourPrompter prompter (true);
1114 prompter.set_title (_("New Playlist"));
1115 prompter.set_prompt (_("Name for new playlist:"));
1116 prompter.set_initial_text (name);
1117 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1118 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1120 switch (prompter.run ()) {
1121 case Gtk::RESPONSE_ACCEPT:
1122 prompter.get_result (name);
1130 if (name.length()) {
1131 tr->use_new_playlist ();
1132 tr->playlist()->set_name (name);
1137 RouteTimeAxisView::clear_playlist ()
1139 boost::shared_ptr<Track> tr = track ();
1140 if (!tr || tr->destructive()) {
1144 boost::shared_ptr<Playlist> pl = tr->playlist();
1149 _editor.clear_playlist (pl);
1153 RouteTimeAxisView::speed_changed ()
1155 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_pixel, this));
1159 RouteTimeAxisView::update_diskstream_display ()
1169 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1171 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1173 /* special case: select/deselect all tracks */
1174 if (_editor.get_selection().selected (this)) {
1175 _editor.get_selection().clear_tracks ();
1177 _editor.select_all_tracks ();
1183 switch (ArdourKeyboard::selection_type (ev->state)) {
1184 case Selection::Toggle:
1185 _editor.get_selection().toggle (this);
1188 case Selection::Set:
1189 _editor.get_selection().set (this);
1192 case Selection::Extend:
1193 _editor.extend_selection_to_track (*this);
1196 case Selection::Add:
1197 _editor.get_selection().add (this);
1203 RouteTimeAxisView::set_selected_points (PointSelection& points)
1205 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1206 (*i)->set_selected_points (points);
1211 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1214 _view->set_selected_regionviews (regions);
1218 /** Add the selectable things that we have to a list.
1219 * @param results List to add things to.
1222 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1227 speed = track()->speed();
1230 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1231 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1233 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1234 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1237 /* pick up visible automation tracks */
1239 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1240 if (!(*i)->hidden()) {
1241 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1247 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1250 _view->get_inverted_selectables (sel, results);
1253 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1254 if (!(*i)->hidden()) {
1255 (*i)->get_inverted_selectables (sel, results);
1263 RouteTimeAxisView::route_group () const
1265 return _route->route_group();
1269 RouteTimeAxisView::name() const
1271 return _route->name();
1274 boost::shared_ptr<Playlist>
1275 RouteTimeAxisView::playlist () const
1277 boost::shared_ptr<Track> tr;
1279 if ((tr = track()) != 0) {
1280 return tr->playlist();
1282 return boost::shared_ptr<Playlist> ();
1287 RouteTimeAxisView::name_entry_changed ()
1289 TimeAxisView::name_entry_changed ();
1291 string x = name_entry->get_text ();
1293 if (x == _route->name()) {
1297 strip_whitespace_edges (x);
1299 if (x.length() == 0) {
1300 name_entry->set_text (_route->name());
1304 if (_session->route_name_internal (x)) {
1305 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1307 name_entry->grab_focus ();
1308 } else if (RouteUI::verify_new_route_name (x)) {
1309 _route->set_name (x);
1311 name_entry->grab_focus ();
1315 boost::shared_ptr<Region>
1316 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1318 boost::shared_ptr<Playlist> pl = playlist ();
1321 return pl->find_next_region (pos, point, dir);
1324 return boost::shared_ptr<Region> ();
1328 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1330 boost::shared_ptr<Playlist> pl = playlist ();
1333 return pl->find_next_region_boundary (pos, dir);
1340 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1342 boost::shared_ptr<Playlist> what_we_got;
1343 boost::shared_ptr<Track> tr = track ();
1344 boost::shared_ptr<Playlist> playlist;
1347 /* route is a bus, not a track */
1351 playlist = tr->playlist();
1353 TimeSelection time (selection.time);
1354 float const speed = tr->speed();
1355 if (speed != 1.0f) {
1356 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1357 (*i).start = session_frame_to_track_frame((*i).start, speed);
1358 (*i).end = session_frame_to_track_frame((*i).end, speed);
1362 playlist->clear_changes ();
1363 playlist->clear_owned_changes ();
1367 if (playlist->cut (time) != 0) {
1368 vector<Command*> cmds;
1369 playlist->rdiff (cmds);
1370 _session->add_commands (cmds);
1372 _session->add_command (new StatefulDiffCommand (playlist));
1377 if ((what_we_got = playlist->cut (time)) != 0) {
1378 _editor.get_cut_buffer().add (what_we_got);
1379 vector<Command*> cmds;
1380 playlist->rdiff (cmds);
1381 _session->add_commands (cmds);
1383 _session->add_command (new StatefulDiffCommand (playlist));
1387 if ((what_we_got = playlist->copy (time)) != 0) {
1388 _editor.get_cut_buffer().add (what_we_got);
1393 if ((what_we_got = playlist->cut (time)) != 0) {
1395 vector<Command*> cmds;
1396 playlist->rdiff (cmds);
1397 _session->add_commands (cmds);
1398 _session->add_command (new StatefulDiffCommand (playlist));
1399 what_we_got->release ();
1406 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1412 boost::shared_ptr<Playlist> pl = playlist ();
1413 PlaylistSelection::iterator p;
1415 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1417 if (p == selection.playlists.end()) {
1421 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1423 if (track()->speed() != 1.0f) {
1424 pos = session_frame_to_track_frame (pos, track()->speed());
1425 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1428 pl->clear_changes ();
1429 pl->paste (*p, pos, times);
1430 _session->add_command (new StatefulDiffCommand (pl));
1436 struct PlaylistSorter {
1437 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1438 return a->sort_id() < b->sort_id();
1443 RouteTimeAxisView::build_playlist_menu ()
1445 using namespace Menu_Helpers;
1451 delete playlist_action_menu;
1452 playlist_action_menu = new Menu;
1453 playlist_action_menu->set_name ("ArdourContextMenu");
1455 MenuList& playlist_items = playlist_action_menu->items();
1456 playlist_action_menu->set_name ("ArdourContextMenu");
1457 playlist_items.clear();
1459 RadioMenuItem::Group playlist_group;
1460 boost::shared_ptr<Track> tr = track ();
1462 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1464 /* sort the playlists */
1466 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1468 /* add the playlists to the menu */
1469 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1470 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1471 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1472 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1474 if (tr->playlist()->id() == (*i)->id()) {
1480 playlist_items.push_back (SeparatorElem());
1481 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1482 playlist_items.push_back (SeparatorElem());
1484 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::select.property_id)) {
1485 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1486 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1489 // Use a label which tells the user what is happening
1490 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1491 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1495 playlist_items.push_back (SeparatorElem());
1496 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1497 playlist_items.push_back (SeparatorElem());
1499 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1503 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1505 assert (is_track());
1507 // exit if we were triggered by deactivating the old playlist
1508 if (!item->get_active()) {
1512 boost::shared_ptr<Playlist> pl (wpl.lock());
1518 if (track()->playlist() == pl) {
1519 // exit when use_playlist is called by the creation of the playlist menu
1520 // or the playlist choice is unchanged
1524 track()->use_playlist (pl);
1526 RouteGroup* rg = route_group();
1528 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1529 std::string group_string = "." + rg->name() + ".";
1531 std::string take_name = pl->name();
1532 std::string::size_type idx = take_name.find(group_string);
1534 if (idx == std::string::npos)
1537 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1539 boost::shared_ptr<RouteList> rl (rg->route_list());
1541 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1542 if ((*i) == this->route()) {
1546 std::string playlist_name = (*i)->name()+group_string+take_name;
1548 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1553 if (track->freeze_state() == Track::Frozen) {
1554 /* Don't change playlists of frozen tracks */
1558 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1560 // No playlist for this track for this take yet, make it
1561 track->use_new_playlist();
1562 track->playlist()->set_name(playlist_name);
1564 track->use_playlist(ipl);
1571 RouteTimeAxisView::update_playlist_tip ()
1573 RouteGroup* rg = route_group ();
1574 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::select.property_id)) {
1575 string group_string = "." + rg->name() + ".";
1577 string take_name = track()->playlist()->name();
1578 string::size_type idx = take_name.find(group_string);
1580 if (idx != string::npos) {
1581 /* find the bit containing the take number / name */
1582 take_name = take_name.substr (idx + group_string.length());
1584 /* set the playlist button tooltip to the take name */
1585 ARDOUR_UI::instance()->set_tip (
1587 string_compose(_("Take: %1.%2"),
1588 Glib::Markup::escape_text(rg->name()),
1589 Glib::Markup::escape_text(take_name))
1596 /* set the playlist button tooltip to the playlist name */
1597 ARDOUR_UI::instance()->set_tip (playlist_button, _("Playlist") + std::string(": ") + Glib::Markup::escape_text(track()->playlist()->name()));
1602 RouteTimeAxisView::show_playlist_selector ()
1604 _editor.playlist_selector().show_for (this);
1608 RouteTimeAxisView::map_frozen ()
1614 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1616 switch (track()->freeze_state()) {
1618 playlist_button.set_sensitive (false);
1619 rec_enable_button->set_sensitive (false);
1622 playlist_button.set_sensitive (true);
1623 rec_enable_button->set_sensitive (true);
1629 RouteTimeAxisView::color_handler ()
1631 //case cTimeStretchOutline:
1632 if (timestretch_rect) {
1633 timestretch_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchOutline());
1635 //case cTimeStretchFill:
1636 if (timestretch_rect) {
1637 timestretch_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_TimeStretchFill());
1643 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1644 * Will add track if necessary.
1647 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1649 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1650 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1653 /* it doesn't exist yet, so we don't care about the button state: just add it */
1654 create_automation_child (param, true);
1657 bool yn = menu->get_active();
1658 bool changed = false;
1660 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1662 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1663 will have done that for us.
1666 if (changed && !no_redraw) {
1674 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1676 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1682 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1684 if (menu && !_hidden) {
1685 ignore_toggle = true;
1686 menu->set_active (false);
1687 ignore_toggle = false;
1690 if (_route && !no_redraw) {
1697 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1699 if (apply_to_selection) {
1700 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1704 /* Show our automation */
1706 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1707 i->second->set_marked_for_display (true);
1709 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1712 menu->set_active(true);
1717 /* Show processor automation */
1719 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1720 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1721 if ((*ii)->view == 0) {
1722 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1725 (*ii)->menu_item->set_active (true);
1738 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1740 if (apply_to_selection) {
1741 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1745 /* Show our automation */
1747 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1748 if (i->second->has_automation()) {
1749 i->second->set_marked_for_display (true);
1751 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1753 menu->set_active(true);
1758 /* Show processor automation */
1760 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1761 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1762 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1763 (*ii)->menu_item->set_active (true);
1775 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1777 if (apply_to_selection) {
1778 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1782 /* Hide our automation */
1784 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1785 i->second->set_marked_for_display (false);
1787 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1790 menu->set_active (false);
1794 /* Hide processor automation */
1796 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1797 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1798 (*ii)->menu_item->set_active (false);
1809 RouteTimeAxisView::region_view_added (RegionView* rv)
1811 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1812 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1813 boost::shared_ptr<AutomationTimeAxisView> atv;
1815 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1820 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1821 (*i)->add_ghost(rv);
1825 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1827 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1833 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1835 parent.remove_processor_automation_node (this);
1839 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1842 remove_child (pan->view);
1846 RouteTimeAxisView::ProcessorAutomationNode*
1847 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1849 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1851 if ((*i)->processor == processor) {
1853 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1854 if ((*ii)->what == what) {
1864 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1866 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1869 ProcessorAutomationNode* pan;
1871 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1872 /* session state may never have been saved with new plugin */
1873 error << _("programming error: ")
1874 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1875 processor->name(), what.type(), (int) what.channel(), what.id() )
1885 boost::shared_ptr<AutomationControl> control
1886 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1888 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1889 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1890 _editor, *this, false, parent_canvas,
1891 processor->describe_parameter (what), processor->name()));
1893 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1895 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1898 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1903 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1906 pan->menu_item->set_active (false);
1915 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1917 boost::shared_ptr<Processor> processor (p.lock ());
1919 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1920 /* The Amp processor is a special case and is dealt with separately */
1924 set<Evoral::Parameter> existing;
1926 processor->what_has_data (existing);
1928 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1930 Evoral::Parameter param (*i);
1931 boost::shared_ptr<AutomationLine> al;
1933 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1936 add_processor_automation_curve (processor, param);
1942 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1944 using namespace Menu_Helpers;
1948 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1950 _automation_tracks[param] = track;
1952 /* existing state overrides "show" argument */
1953 string s = track->gui_property ("visible");
1955 show = string_is_affirmative (s);
1958 /* this might or might not change the visibility status, so don't rely on it */
1959 track->set_marked_for_display (show);
1961 if (show && !no_redraw) {
1965 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1966 /* MIDI-related parameters are always in the menu, there's no
1967 reason to rebuild the menu just because we added a automation
1968 lane for one of them. But if we add a non-MIDI automation
1969 lane, then we need to invalidate the display menu.
1971 delete display_menu;
1977 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1979 boost::shared_ptr<Processor> processor (p.lock ());
1981 if (!processor || !processor->display_to_user ()) {
1985 /* we use this override to veto the Amp processor from the plugin menu,
1986 as its automation lane can be accessed using the special "Fader" menu
1990 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1994 using namespace Menu_Helpers;
1995 ProcessorAutomationInfo *rai;
1996 list<ProcessorAutomationInfo*>::iterator x;
1998 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2000 if (automatable.empty()) {
2004 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2005 if ((*x)->processor == processor) {
2010 if (x == processor_automation.end()) {
2012 rai = new ProcessorAutomationInfo (processor);
2013 processor_automation.push_back (rai);
2021 /* any older menu was deleted at the top of processors_changed()
2022 when we cleared the subplugin menu.
2025 rai->menu = manage (new Menu);
2026 MenuList& items = rai->menu->items();
2027 rai->menu->set_name ("ArdourContextMenu");
2031 std::set<Evoral::Parameter> has_visible_automation;
2032 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2034 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2036 ProcessorAutomationNode* pan;
2037 Gtk::CheckMenuItem* mitem;
2039 string name = processor->describe_parameter (*i);
2041 items.push_back (CheckMenuElem (name));
2042 mitem = dynamic_cast<Gtk::CheckMenuItem*> (&items.back());
2044 _subplugin_menu_map[*i] = mitem;
2046 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2047 mitem->set_active(true);
2050 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2054 pan = new ProcessorAutomationNode (*i, mitem, *this);
2056 rai->lines.push_back (pan);
2060 pan->menu_item = mitem;
2064 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2067 /* add the menu for this processor, because the subplugin
2068 menu is always cleared at the top of processors_changed().
2069 this is the result of some poor design in gtkmm and/or
2073 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2078 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2079 RouteTimeAxisView::ProcessorAutomationNode* pan)
2081 bool showit = pan->menu_item->get_active();
2082 bool redraw = false;
2084 if (pan->view == 0 && showit) {
2085 add_processor_automation_curve (rai->processor, pan->what);
2089 if (pan->view && pan->view->set_marked_for_display (showit)) {
2093 if (redraw && !no_redraw) {
2099 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2101 if (c.type == RouteProcessorChange::MeterPointChange) {
2102 /* nothing to do if only the meter point has changed */
2106 using namespace Menu_Helpers;
2108 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2109 (*i)->valid = false;
2112 setup_processor_menu_and_curves ();
2114 bool deleted_processor_automation = false;
2116 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2118 list<ProcessorAutomationInfo*>::iterator tmp;
2126 processor_automation.erase (i);
2127 deleted_processor_automation = true;
2134 if (deleted_processor_automation && !no_redraw) {
2139 boost::shared_ptr<AutomationLine>
2140 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2142 ProcessorAutomationNode* pan;
2144 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2150 return boost::shared_ptr<AutomationLine>();
2154 RouteTimeAxisView::reset_processor_automation_curves ()
2156 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2162 RouteTimeAxisView::can_edit_name () const
2164 /* we do not allow track name changes if it is record enabled
2166 return !_route->record_enabled();
2170 RouteTimeAxisView::update_rec_display ()
2172 RouteUI::update_rec_display ();
2176 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2178 if (_ignore_set_layer_display) {
2182 if (apply_to_selection) {
2183 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2187 _view->set_layer_display (d);
2190 set_gui_property (X_("layer-display"), enum_2_string (d));
2195 RouteTimeAxisView::layer_display () const
2198 return _view->layer_display ();
2201 /* we don't know, since we don't have a _view, so just return something */
2207 boost::shared_ptr<AutomationTimeAxisView>
2208 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2210 AutomationTracks::iterator i = _automation_tracks.find(param);
2211 if (i != _automation_tracks.end()) {
2214 return boost::shared_ptr<AutomationTimeAxisView>();
2219 RouteTimeAxisView::fast_update ()
2221 gm.get_level_meter().update_meters ();
2225 RouteTimeAxisView::hide_meter ()
2228 gm.get_level_meter().hide_meters ();
2232 RouteTimeAxisView::show_meter ()
2238 RouteTimeAxisView::reset_meter ()
2240 if (Config->get_show_track_meters()) {
2241 int meter_width = 3;
2242 if (_route && _route->shared_peak_meter()->input_streams().n_total() == 1) {
2245 gm.get_level_meter().setup_meters (height - 9, meter_width);
2252 RouteTimeAxisView::clear_meter ()
2254 gm.get_level_meter().clear_meters ();
2258 RouteTimeAxisView::meter_changed ()
2260 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2262 if (_route && !no_redraw) {
2265 // reset peak when meter point changes
2266 gm.reset_peak_display();
2270 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2273 if (_route && !no_redraw) {
2279 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2281 using namespace Menu_Helpers;
2283 if (!_underlay_streams.empty()) {
2284 MenuList& parent_items = parent_menu->items();
2285 Menu* gs_menu = manage (new Menu);
2286 gs_menu->set_name ("ArdourContextMenu");
2287 MenuList& gs_items = gs_menu->items();
2289 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2291 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2292 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2293 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2299 RouteTimeAxisView::set_underlay_state()
2301 if (!underlay_xml_node) {
2305 XMLNodeList nlist = underlay_xml_node->children();
2306 XMLNodeConstIterator niter;
2307 XMLNode *child_node;
2309 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2310 child_node = *niter;
2312 if (child_node->name() != "Underlay") {
2316 XMLProperty* prop = child_node->property ("id");
2318 PBD::ID id (prop->value());
2320 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2323 add_underlay(v->view(), false);
2332 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2338 RouteTimeAxisView& other = v->trackview();
2340 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2341 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2342 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2346 _underlay_streams.push_back(v);
2347 other._underlay_mirrors.push_back(this);
2349 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2351 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2353 if (!underlay_xml_node) {
2354 underlay_xml_node = xml_node->add_child("Underlays");
2357 XMLNode* node = underlay_xml_node->add_child("Underlay");
2358 XMLProperty* prop = node->add_property("id");
2359 prop->set_value(v->trackview().route()->id().to_s());
2366 RouteTimeAxisView::remove_underlay (StreamView* v)
2372 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2373 RouteTimeAxisView& other = v->trackview();
2375 if (it != _underlay_streams.end()) {
2376 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2378 if (gm == other._underlay_mirrors.end()) {
2379 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2383 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2385 _underlay_streams.erase(it);
2386 other._underlay_mirrors.erase(gm);
2388 if (underlay_xml_node) {
2389 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2395 RouteTimeAxisView::set_button_names ()
2397 if (_route && _route->solo_safe()) {
2398 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
2400 solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive));
2402 if (Config->get_solo_control_is_listen_control()) {
2403 switch (Config->get_listen_position()) {
2404 case AfterFaderListen:
2405 solo_button->set_text (_("A"));
2406 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2408 case PreFaderListen:
2409 solo_button->set_text (_("P"));
2410 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2414 solo_button->set_text (_("s"));
2415 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2417 mute_button->set_text (_("m"));
2421 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2423 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2424 if (i != _main_automation_menu_map.end()) {
2428 i = _subplugin_menu_map.find (param);
2429 if (i != _subplugin_menu_map.end()) {
2437 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2439 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2441 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2445 gain_track.reset (new AutomationTimeAxisView (_session,
2446 _route, _route->amp(), c, param,
2451 _route->amp()->describe_parameter(param)));
2454 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2457 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2461 void add_region_to_list (RegionView* rv, RegionList* l)
2463 l->push_back (rv->region());
2467 RouteTimeAxisView::combine_regions ()
2469 /* as of may 2011, we do not offer uncombine for MIDI tracks
2472 if (!is_audio_track()) {
2480 RegionList selected_regions;
2481 boost::shared_ptr<Playlist> playlist = track()->playlist();
2483 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2485 if (selected_regions.size() < 2) {
2489 playlist->clear_changes ();
2490 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2492 _session->add_command (new StatefulDiffCommand (playlist));
2493 /* make the new region be selected */
2495 return _view->find_view (compound_region);
2499 RouteTimeAxisView::uncombine_regions ()
2501 /* as of may 2011, we do not offer uncombine for MIDI tracks
2503 if (!is_audio_track()) {
2511 RegionList selected_regions;
2512 boost::shared_ptr<Playlist> playlist = track()->playlist();
2514 /* have to grab selected regions first because the uncombine is going
2515 * to change that in the middle of the list traverse
2518 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2520 playlist->clear_changes ();
2522 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2523 playlist->uncombine (*i);
2526 _session->add_command (new StatefulDiffCommand (playlist));
2530 RouteTimeAxisView::state_id() const
2532 return string_compose ("rtav %1", _route->id().to_s());
2537 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2539 TimeAxisView::remove_child (c);
2541 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2543 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2544 if (i->second == a) {
2545 _automation_tracks.erase (i);