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/audioplaylist.h"
47 #include "ardour/diskstream.h"
48 #include "ardour/event_type_map.h"
49 #include "ardour/ladspa_plugin.h"
50 #include "ardour/location.h"
51 #include "ardour/panner.h"
52 #include "ardour/playlist.h"
53 #include "ardour/playlist.h"
54 #include "ardour/processor.h"
55 #include "ardour/profile.h"
56 #include "ardour/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
65 #include "ardour_button.h"
67 #include "global_signals.h"
68 #include "route_time_axis.h"
69 #include "automation_time_axis.h"
70 #include "canvas_impl.h"
72 #include "gui_thread.h"
74 #include "playlist_selector.h"
75 #include "point_selection.h"
77 #include "public_editor.h"
78 #include "region_view.h"
79 #include "rgb_macros.h"
80 #include "selection.h"
81 #include "simplerect.h"
82 #include "streamview.h"
84 #include "route_group_menu.h"
86 #include "ardour/track.h"
90 using namespace ARDOUR;
92 using namespace Gtkmm2ext;
94 using namespace Editing;
98 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
101 RouteTimeAxisView::setup_slider_pix ()
103 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
104 throw failed_constructor ();
108 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
111 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
113 , parent_canvas (canvas)
115 , button_table (3, 3)
116 , route_group_button (_("g"))
117 , playlist_button (_("p"))
118 , automation_button (_("a"))
119 , automation_action_menu (0)
120 , plugins_submenu_item (0)
121 , route_group_menu (0)
122 , playlist_action_menu (0)
124 , color_mode_menu (0)
125 , gm (sess, slider, true, 115)
130 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
132 RouteUI::set_route (rt);
134 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
135 gm.get_level_meter().set_no_show_all();
136 gm.get_level_meter().setup_meters(50);
138 string str = gui_property ("height");
140 set_height (atoi (str));
142 set_height (preset_height (HeightNormal));
145 if (!_route->is_hidden()) {
146 if (gui_property ("visible").empty()) {
147 set_gui_property ("visible", true);
150 set_gui_property ("visible", false);
154 update_solo_display ();
156 timestretch_rect = 0;
159 ignore_toggle = false;
161 route_group_button.set_name ("route button");
162 playlist_button.set_name ("route button");
163 automation_button.set_name ("route button");
165 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
166 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
167 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
173 switch (track()->mode()) {
175 case ARDOUR::NonLayered:
176 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
178 case ARDOUR::Destructive:
179 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
183 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
185 if (is_midi_track()) {
186 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
188 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
191 rec_enable_button->set_sensitive (_session->writable());
194 controls_hbox.pack_start(gm.get_level_meter(), false, false);
195 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
196 _route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
197 _route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
199 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
201 if (!_route->is_master()) {
202 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
205 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
208 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
209 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
210 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
211 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
213 if (is_midi_track()) {
214 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
216 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
221 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
223 if (is_track() && track()->mode() == ARDOUR::Normal) {
224 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
229 _route->processors_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
230 _route->PropertyChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
234 str = gui_property ("layer-display");
236 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
239 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
240 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
242 /* pick up the correct freeze state */
246 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
247 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
248 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
250 PropertyList* plist = new PropertyList();
252 plist->add (ARDOUR::Properties::edit, true);
253 plist->add (ARDOUR::Properties::mute, true);
254 plist->add (ARDOUR::Properties::solo, true);
256 route_group_menu = new RouteGroupMenu (_session, plist);
258 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
259 gm.get_gain_slider().set_name ("TrackGainFader");
261 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
267 RouteTimeAxisView::~RouteTimeAxisView ()
269 CatchDeletion (this);
271 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
275 delete playlist_action_menu;
276 playlist_action_menu = 0;
281 _automation_tracks.clear ();
283 delete route_group_menu;
287 RouteTimeAxisView::post_construct ()
289 /* map current state of the route */
291 update_diskstream_display ();
292 setup_processor_menu_and_curves ();
293 reset_processor_automation_curves ();
296 /** Set up the processor menu for the current set of processors, and
297 * display automation curves for any parameters which have data.
300 RouteTimeAxisView::setup_processor_menu_and_curves ()
302 _subplugin_menu_map.clear ();
303 subplugin_menu.items().clear ();
304 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
305 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
309 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
311 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
312 if (_route->route_group()) {
313 _route->route_group()->remove (_route);
319 r.push_back (route ());
321 route_group_menu->build (r);
322 route_group_menu->menu()->popup (ev->button, ev->time);
328 RouteTimeAxisView::playlist_changed ()
334 RouteTimeAxisView::label_view ()
336 string x = _route->name();
338 if (x != name_entry.get_text()) {
339 name_entry.set_text (x);
342 if (x != name_label.get_text()) {
343 name_label.set_text (x);
346 ARDOUR_UI::instance()->set_tip (name_entry, x);
350 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
352 if (what_changed.contains (ARDOUR::Properties::name)) {
358 RouteTimeAxisView::take_name_changed (void *src)
366 RouteTimeAxisView::playlist_click ()
368 build_playlist_menu ();
369 conditionally_add_to_selection ();
370 playlist_action_menu->popup (1, gtk_get_current_event_time());
374 RouteTimeAxisView::automation_click ()
376 conditionally_add_to_selection ();
377 build_automation_action_menu (false);
378 automation_action_menu->popup (1, gtk_get_current_event_time());
382 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
384 using namespace Menu_Helpers;
386 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
387 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
390 detach_menu (subplugin_menu);
392 _main_automation_menu_map.clear ();
393 delete automation_action_menu;
394 automation_action_menu = new Menu;
396 MenuList& items = automation_action_menu->items();
398 automation_action_menu->set_name ("ArdourContextMenu");
400 items.push_back (MenuElem (_("Show All Automation"),
401 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
403 items.push_back (MenuElem (_("Show Existing Automation"),
404 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
406 items.push_back (MenuElem (_("Hide All Automation"),
407 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
409 items.push_back (SeparatorElem ());
411 /* Attach the plugin submenu. It may have previously been used elsewhere,
412 so it was detached above */
414 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
415 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
419 RouteTimeAxisView::build_display_menu ()
421 using namespace Menu_Helpers;
425 TimeAxisView::build_display_menu ();
427 /* now fill it with our stuff */
429 MenuList& items = display_menu->items();
430 display_menu->set_name ("ArdourContextMenu");
432 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
435 detach_menu (*_size_menu);
438 items.push_back (MenuElem (_("Height"), *_size_menu));
440 items.push_back (SeparatorElem());
442 if (!Profile->get_sae()) {
443 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
444 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
445 items.push_back (SeparatorElem());
448 // Hook for derived classes to add type specific stuff
449 append_extra_display_menu_items ();
453 Menu* layers_menu = manage (new Menu);
454 MenuList &layers_items = layers_menu->items();
455 layers_menu->set_name("ArdourContextMenu");
457 RadioMenuItem::Group layers_group;
459 /* Find out how many overlaid/stacked tracks we have in the selection */
463 TrackSelection const & s = _editor.get_selection().tracks;
464 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
465 StreamView* v = (*i)->view ();
470 switch (v->layer_display ()) {
481 /* We're not connecting to signal_toggled() here; in the case where these two items are
482 set to be in the `inconsistent' state, it seems that one or other will end up active
483 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
484 select the active one, no toggled signal is emitted so nothing happens.
487 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
488 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
489 i->set_active (overlaid != 0 && stacked == 0);
490 i->set_inconsistent (overlaid != 0 && stacked != 0);
491 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
493 layers_items.push_back (
494 RadioMenuElem (layers_group, _("Stacked"),
495 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
498 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
499 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
500 i->set_active (overlaid == 0 && stacked != 0);
501 i->set_inconsistent (overlaid != 0 && stacked != 0);
503 items.push_back (MenuElem (_("Layers"), *layers_menu));
505 if (!Profile->get_sae()) {
507 Menu* alignment_menu = manage (new Menu);
508 MenuList& alignment_items = alignment_menu->items();
509 alignment_menu->set_name ("ArdourContextMenu");
511 RadioMenuItem::Group align_group;
513 /* Same verbose hacks as for the layering options above */
519 boost::shared_ptr<Track> first_track;
521 TrackSelection const & s = _editor.get_selection().tracks;
522 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
523 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
524 if (!r || !r->is_track ()) {
529 first_track = r->track();
532 switch (r->track()->alignment_choice()) {
536 switch (r->track()->alignment_style()) {
537 case ExistingMaterial:
545 case UseExistingMaterial:
561 inconsistent = false;
570 if (!inconsistent && first_track) {
572 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
573 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
574 i->set_active (automatic != 0 && existing == 0 && capture == 0);
575 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
577 switch (first_track->alignment_choice()) {
579 switch (first_track->alignment_style()) {
580 case ExistingMaterial:
581 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
584 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
592 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
593 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
594 i->set_active (existing != 0 && capture == 0 && automatic == 0);
595 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
597 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
598 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
599 i->set_active (existing == 0 && capture != 0 && automatic == 0);
600 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
602 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
608 Menu* mode_menu = manage (new Menu);
609 MenuList& mode_items = mode_menu->items ();
610 mode_menu->set_name ("ArdourContextMenu");
612 RadioMenuItem::Group mode_group;
618 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
619 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
620 if (!r || !r->is_track ()) {
624 switch (r->track()->mode()) {
637 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
638 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
639 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
640 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
641 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
643 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
644 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
645 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
646 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
647 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
649 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
650 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
651 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
652 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
653 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
655 items.push_back (MenuElem (_("Mode"), *mode_menu));
658 color_mode_menu = build_color_mode_menu();
659 if (color_mode_menu) {
660 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
663 items.push_back (SeparatorElem());
665 build_playlist_menu ();
666 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
667 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
669 route_group_menu->detach ();
672 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
673 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
675 r.push_back (rtv->route ());
680 r.push_back (route ());
683 route_group_menu->build (r);
684 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
686 build_automation_action_menu (true);
687 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
689 items.push_back (SeparatorElem());
694 TrackSelection const & s = _editor.get_selection().tracks;
695 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
696 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
701 if (r->route()->active()) {
708 items.push_back (CheckMenuElem (_("Active")));
709 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
710 bool click_sets_active = true;
711 if (active > 0 && inactive == 0) {
712 i->set_active (true);
713 click_sets_active = false;
714 } else if (active > 0 && inactive > 0) {
715 i->set_inconsistent (true);
717 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
719 items.push_back (SeparatorElem());
720 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
721 if (!Profile->get_sae()) {
722 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
724 items.push_front (SeparatorElem());
725 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
730 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
732 if (apply_to_selection) {
733 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
738 if (!track()->can_use_mode (mode, needs_bounce)) {
744 cerr << "would bounce this one\n";
749 track()->set_mode (mode);
751 rec_enable_button->remove ();
754 case ARDOUR::NonLayered:
756 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
757 rec_enable_button->set_text (string());
759 case ARDOUR::Destructive:
760 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
761 rec_enable_button->set_text (string());
765 rec_enable_button->show_all ();
770 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
776 TimeAxisView::show_timestretch (start, end);
786 /* check that the time selection was made in our route, or our route group.
787 remember that route_group() == 0 implies the route is *not* in a edit group.
790 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
791 /* this doesn't apply to us */
795 /* ignore it if our edit group is not active */
797 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
802 if (timestretch_rect == 0) {
803 timestretch_rect = new SimpleRect (*canvas_display ());
804 timestretch_rect->property_x1() = 0.0;
805 timestretch_rect->property_y1() = 0.0;
806 timestretch_rect->property_x2() = 0.0;
807 timestretch_rect->property_y2() = 0.0;
808 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
809 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
812 timestretch_rect->show ();
813 timestretch_rect->raise_to_top ();
815 x1 = start / _editor.get_current_zoom();
816 x2 = (end - 1) / _editor.get_current_zoom();
817 y2 = current_height() - 2;
819 timestretch_rect->property_x1() = x1;
820 timestretch_rect->property_y1() = 1.0;
821 timestretch_rect->property_x2() = x2;
822 timestretch_rect->property_y2() = y2;
826 RouteTimeAxisView::hide_timestretch ()
828 TimeAxisView::hide_timestretch ();
830 if (timestretch_rect) {
831 timestretch_rect->hide ();
836 RouteTimeAxisView::show_selection (TimeSelection& ts)
840 /* ignore it if our edit group is not active or if the selection was started
841 in some other track or route group (remember that route_group() == 0 means
842 that the track is not in an route group).
845 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
846 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
852 TimeAxisView::show_selection (ts);
856 RouteTimeAxisView::set_height (uint32_t h)
859 bool height_changed = (height == 0) || (h != height);
860 gm.get_level_meter().setup_meters (gmlen);
862 TimeAxisView::set_height (h);
865 _view->set_height ((double) current_height());
868 if (height >= preset_height (HeightNormal)) {
872 gm.get_gain_slider().show();
874 if (!_route || _route->is_monitor()) {
879 if (rec_enable_button)
880 rec_enable_button->show();
882 route_group_button.show();
883 automation_button.show();
885 if (is_track() && track()->mode() == ARDOUR::Normal) {
886 playlist_button.show();
893 gm.get_gain_slider().hide();
895 if (!_route || _route->is_monitor()) {
900 if (rec_enable_button)
901 rec_enable_button->show();
903 route_group_button.hide ();
904 automation_button.hide ();
906 if (is_track() && track()->mode() == ARDOUR::Normal) {
907 playlist_button.hide ();
912 if (height_changed && !no_redraw) {
913 /* only emit the signal if the height really changed */
919 RouteTimeAxisView::route_color_changed ()
922 _view->apply_color (color(), StreamView::RegionColor);
927 RouteTimeAxisView::reset_samples_per_unit ()
929 set_samples_per_unit (_editor.get_current_zoom());
933 RouteTimeAxisView::horizontal_position_changed ()
936 _view->horizontal_position_changed ();
941 RouteTimeAxisView::set_samples_per_unit (double spu)
946 speed = track()->speed();
950 _view->set_samples_per_unit (spu * speed);
953 TimeAxisView::set_samples_per_unit (spu * speed);
957 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
959 if (!mitem->get_active()) {
960 /* this is one of the two calls made when these radio menu items change status. this one
961 is for the item that became inactive, and we want to ignore it.
966 if (apply_to_selection) {
967 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
970 track()->set_align_choice (choice);
976 RouteTimeAxisView::rename_current_playlist ()
978 ArdourPrompter prompter (true);
981 boost::shared_ptr<Track> tr = track();
982 if (!tr || tr->destructive()) {
986 boost::shared_ptr<Playlist> pl = tr->playlist();
991 prompter.set_title (_("Rename Playlist"));
992 prompter.set_prompt (_("New name for playlist:"));
993 prompter.set_initial_text (pl->name());
994 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
995 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
997 switch (prompter.run ()) {
998 case Gtk::RESPONSE_ACCEPT:
999 prompter.get_result (name);
1000 if (name.length()) {
1001 pl->set_name (name);
1011 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1013 std::string ret (basename);
1015 std::string const group_string = "." + route_group()->name() + ".";
1017 // iterate through all playlists
1019 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1020 std::string tmp = (*i)->name();
1022 std::string::size_type idx = tmp.find(group_string);
1023 // find those which belong to this group
1024 if (idx != string::npos) {
1025 tmp = tmp.substr(idx + group_string.length());
1027 // and find the largest current number
1028 int x = atoi(tmp.c_str());
1029 if (x > maxnumber) {
1038 snprintf (buf, sizeof(buf), "%d", maxnumber);
1040 ret = this->name() + "." + route_group()->name () + "." + buf;
1046 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1050 boost::shared_ptr<Track> tr = track ();
1051 if (!tr || tr->destructive()) {
1055 boost::shared_ptr<const Playlist> pl = tr->playlist();
1062 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1063 name = resolve_new_group_playlist_name(name, playlists_before_op);
1066 while (_session->playlists->by_name(name)) {
1067 name = Playlist::bump_name (name, *_session);
1070 // TODO: The prompter "new" button should be de-activated if the user
1071 // specifies a playlist name which already exists in the session.
1075 ArdourPrompter prompter (true);
1077 prompter.set_title (_("New Copy Playlist"));
1078 prompter.set_prompt (_("Name for new playlist:"));
1079 prompter.set_initial_text (name);
1080 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1081 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1082 prompter.show_all ();
1084 switch (prompter.run ()) {
1085 case Gtk::RESPONSE_ACCEPT:
1086 prompter.get_result (name);
1094 if (name.length()) {
1095 tr->use_copy_playlist ();
1096 tr->playlist()->set_name (name);
1101 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1105 boost::shared_ptr<Track> tr = track ();
1106 if (!tr || tr->destructive()) {
1110 boost::shared_ptr<const Playlist> pl = tr->playlist();
1117 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1118 name = resolve_new_group_playlist_name(name,playlists_before_op);
1121 while (_session->playlists->by_name(name)) {
1122 name = Playlist::bump_name (name, *_session);
1128 ArdourPrompter prompter (true);
1130 prompter.set_title (_("New Playlist"));
1131 prompter.set_prompt (_("Name for new playlist:"));
1132 prompter.set_initial_text (name);
1133 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1134 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1136 switch (prompter.run ()) {
1137 case Gtk::RESPONSE_ACCEPT:
1138 prompter.get_result (name);
1146 if (name.length()) {
1147 tr->use_new_playlist ();
1148 tr->playlist()->set_name (name);
1153 RouteTimeAxisView::clear_playlist ()
1155 boost::shared_ptr<Track> tr = track ();
1156 if (!tr || tr->destructive()) {
1160 boost::shared_ptr<Playlist> pl = tr->playlist();
1165 _editor.clear_playlist (pl);
1169 RouteTimeAxisView::speed_changed ()
1171 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1175 RouteTimeAxisView::update_diskstream_display ()
1185 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1187 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1189 /* special case: select/deselect all tracks */
1190 if (_editor.get_selection().selected (this)) {
1191 _editor.get_selection().clear_tracks ();
1193 _editor.select_all_tracks ();
1199 switch (ArdourKeyboard::selection_type (ev->state)) {
1200 case Selection::Toggle:
1201 _editor.get_selection().toggle (this);
1204 case Selection::Set:
1205 _editor.get_selection().set (this);
1208 case Selection::Extend:
1209 _editor.extend_selection_to_track (*this);
1212 case Selection::Add:
1213 _editor.get_selection().add (this);
1219 RouteTimeAxisView::set_selected_points (PointSelection& points)
1221 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1222 (*i)->set_selected_points (points);
1227 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1230 _view->set_selected_regionviews (regions);
1234 /** Add the selectable things that we have to a list.
1235 * @param results List to add things to.
1238 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1243 speed = track()->speed();
1246 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1247 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1249 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1250 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1253 /* pick up visible automation tracks */
1255 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1256 if (!(*i)->hidden()) {
1257 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1263 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1266 _view->get_inverted_selectables (sel, results);
1269 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1270 if (!(*i)->hidden()) {
1271 (*i)->get_inverted_selectables (sel, results);
1279 RouteTimeAxisView::route_group () const
1281 return _route->route_group();
1285 RouteTimeAxisView::name() const
1287 return _route->name();
1290 boost::shared_ptr<Playlist>
1291 RouteTimeAxisView::playlist () const
1293 boost::shared_ptr<Track> tr;
1295 if ((tr = track()) != 0) {
1296 return tr->playlist();
1298 return boost::shared_ptr<Playlist> ();
1303 RouteTimeAxisView::name_entry_changed ()
1305 string x = name_entry.get_text ();
1307 if (x == _route->name()) {
1311 strip_whitespace_edges (x);
1313 if (x.length() == 0) {
1314 name_entry.set_text (_route->name());
1318 if (_session->route_name_internal (x)) {
1319 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1321 name_entry.grab_focus ();
1322 } else if (RouteUI::verify_new_route_name (x)) {
1323 _route->set_name (x);
1325 name_entry.grab_focus ();
1329 boost::shared_ptr<Region>
1330 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1332 boost::shared_ptr<Playlist> pl = playlist ();
1335 return pl->find_next_region (pos, point, dir);
1338 return boost::shared_ptr<Region> ();
1342 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1344 boost::shared_ptr<Playlist> pl = playlist ();
1347 return pl->find_next_region_boundary (pos, dir);
1354 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1356 boost::shared_ptr<Playlist> what_we_got;
1357 boost::shared_ptr<Track> tr = track ();
1358 boost::shared_ptr<Playlist> playlist;
1361 /* route is a bus, not a track */
1365 playlist = tr->playlist();
1367 TimeSelection time (selection.time);
1368 float const speed = tr->speed();
1369 if (speed != 1.0f) {
1370 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1371 (*i).start = session_frame_to_track_frame((*i).start, speed);
1372 (*i).end = session_frame_to_track_frame((*i).end, speed);
1376 playlist->clear_changes ();
1377 playlist->clear_owned_changes ();
1381 if (playlist->cut (time) != 0) {
1382 vector<Command*> cmds;
1383 playlist->rdiff (cmds);
1384 _session->add_commands (cmds);
1386 _session->add_command (new StatefulDiffCommand (playlist));
1391 if ((what_we_got = playlist->cut (time)) != 0) {
1392 _editor.get_cut_buffer().add (what_we_got);
1393 vector<Command*> cmds;
1394 playlist->rdiff (cmds);
1395 _session->add_commands (cmds);
1397 _session->add_command (new StatefulDiffCommand (playlist));
1401 if ((what_we_got = playlist->copy (time)) != 0) {
1402 _editor.get_cut_buffer().add (what_we_got);
1407 if ((what_we_got = playlist->cut (time)) != 0) {
1409 vector<Command*> cmds;
1410 playlist->rdiff (cmds);
1411 _session->add_commands (cmds);
1412 _session->add_command (new StatefulDiffCommand (playlist));
1413 what_we_got->release ();
1420 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1426 boost::shared_ptr<Playlist> pl = playlist ();
1427 PlaylistSelection::iterator p;
1429 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1431 if (p == selection.playlists.end()) {
1435 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1437 if (track()->speed() != 1.0f) {
1438 pos = session_frame_to_track_frame (pos, track()->speed());
1439 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1442 pl->clear_changes ();
1443 pl->paste (*p, pos, times);
1444 _session->add_command (new StatefulDiffCommand (pl));
1450 struct PlaylistSorter {
1451 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1452 return a->sort_id() < b->sort_id();
1457 RouteTimeAxisView::build_playlist_menu ()
1459 using namespace Menu_Helpers;
1465 delete playlist_action_menu;
1466 playlist_action_menu = new Menu;
1467 playlist_action_menu->set_name ("ArdourContextMenu");
1469 MenuList& playlist_items = playlist_action_menu->items();
1470 playlist_action_menu->set_name ("ArdourContextMenu");
1471 playlist_items.clear();
1473 RadioMenuItem::Group playlist_group;
1474 boost::shared_ptr<Track> tr = track ();
1476 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1478 /* sort the playlists */
1480 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1482 /* add the playlists to the menu */
1483 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1484 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1485 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1486 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1488 if (tr->playlist()->id() == (*i)->id()) {
1494 playlist_items.push_back (SeparatorElem());
1495 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1496 playlist_items.push_back (SeparatorElem());
1498 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1499 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1500 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1503 // Use a label which tells the user what is happening
1504 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1505 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1509 playlist_items.push_back (SeparatorElem());
1510 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1511 playlist_items.push_back (SeparatorElem());
1513 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1517 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1519 assert (is_track());
1521 // exit if we were triggered by deactivating the old playlist
1522 if (!item->get_active()) {
1526 boost::shared_ptr<Playlist> pl (wpl.lock());
1532 if (track()->playlist() == pl) {
1533 // exit when use_playlist is called by the creation of the playlist menu
1534 // or the playlist choice is unchanged
1538 track()->use_playlist (pl);
1540 RouteGroup* rg = route_group();
1542 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1543 std::string group_string = "." + rg->name() + ".";
1545 std::string take_name = pl->name();
1546 std::string::size_type idx = take_name.find(group_string);
1548 if (idx == std::string::npos)
1551 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1553 boost::shared_ptr<RouteList> rl (rg->route_list());
1555 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1556 if ( (*i) == this->route()) {
1560 std::string playlist_name = (*i)->name()+group_string+take_name;
1562 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1567 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1569 // No playlist for this track for this take yet, make it
1570 track->use_new_playlist();
1571 track->playlist()->set_name(playlist_name);
1573 track->use_playlist(ipl);
1580 RouteTimeAxisView::show_playlist_selector ()
1582 _editor.playlist_selector().show_for (this);
1586 RouteTimeAxisView::map_frozen ()
1592 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1594 switch (track()->freeze_state()) {
1596 playlist_button.set_sensitive (false);
1597 rec_enable_button->set_sensitive (false);
1600 playlist_button.set_sensitive (true);
1601 rec_enable_button->set_sensitive (true);
1607 RouteTimeAxisView::color_handler ()
1609 //case cTimeStretchOutline:
1610 if (timestretch_rect) {
1611 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1613 //case cTimeStretchFill:
1614 if (timestretch_rect) {
1615 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1621 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1622 * Will add track if necessary.
1625 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1627 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1628 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1631 /* it doesn't exist yet, so we don't care about the button state: just add it */
1632 create_automation_child (param, true);
1635 bool yn = menu->get_active();
1636 bool changed = false;
1638 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1640 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1641 will have done that for us.
1644 if (changed && !no_redraw) {
1652 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1654 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1660 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1662 if (menu && !_hidden) {
1663 ignore_toggle = true;
1664 menu->set_active (false);
1665 ignore_toggle = false;
1668 if (_route && !no_redraw) {
1675 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1677 if (apply_to_selection) {
1678 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1682 /* Show our automation */
1684 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1685 i->second->set_marked_for_display (true);
1687 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1690 menu->set_active(true);
1695 /* Show processor automation */
1697 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1698 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1699 if ((*ii)->view == 0) {
1700 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1703 (*ii)->menu_item->set_active (true);
1716 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1718 if (apply_to_selection) {
1719 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1723 /* Show our automation */
1725 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1726 if (i->second->has_automation()) {
1727 i->second->set_marked_for_display (true);
1729 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1731 menu->set_active(true);
1736 /* Show processor automation */
1738 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1739 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1740 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1741 (*ii)->menu_item->set_active (true);
1753 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1755 if (apply_to_selection) {
1756 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1760 /* Hide our automation */
1762 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1763 i->second->set_marked_for_display (false);
1765 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1768 menu->set_active (false);
1772 /* Hide processor automation */
1774 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1775 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1776 (*ii)->menu_item->set_active (false);
1787 RouteTimeAxisView::region_view_added (RegionView* rv)
1789 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1790 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1791 boost::shared_ptr<AutomationTimeAxisView> atv;
1793 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1798 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1799 (*i)->add_ghost(rv);
1803 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1805 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1811 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1813 parent.remove_processor_automation_node (this);
1817 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1820 remove_child (pan->view);
1824 RouteTimeAxisView::ProcessorAutomationNode*
1825 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1827 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1829 if ((*i)->processor == processor) {
1831 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1832 if ((*ii)->what == what) {
1842 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1844 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1847 ProcessorAutomationNode* pan;
1849 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1850 /* session state may never have been saved with new plugin */
1851 error << _("programming error: ")
1852 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1853 processor->name(), what.type(), (int) what.channel(), what.id() )
1863 boost::shared_ptr<AutomationControl> control
1864 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1866 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1867 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1868 _editor, *this, false, parent_canvas,
1869 processor->describe_parameter (what), processor->name()));
1871 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1873 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1876 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1881 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1884 pan->menu_item->set_active (false);
1893 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1895 boost::shared_ptr<Processor> processor (p.lock ());
1897 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1898 /* The Amp processor is a special case and is dealt with separately */
1902 set<Evoral::Parameter> existing;
1904 processor->what_has_data (existing);
1906 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1908 Evoral::Parameter param (*i);
1909 boost::shared_ptr<AutomationLine> al;
1911 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1914 add_processor_automation_curve (processor, param);
1920 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1922 using namespace Menu_Helpers;
1926 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1928 _automation_tracks[param] = track;
1930 /* existing state overrides "show" argument */
1931 string s = track->gui_property ("visible");
1933 show = string_is_affirmative (s);
1936 /* this might or might not change the visibility status, so don't rely on it */
1937 track->set_marked_for_display (show);
1939 if (show && !no_redraw) {
1943 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1944 /* MIDI-related parameters are always in the menu, there's no
1945 reason to rebuild the menu just because we added a automation
1946 lane for one of them. But if we add a non-MIDI automation
1947 lane, then we need to invalidate the display menu.
1949 delete display_menu;
1955 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1957 boost::shared_ptr<Processor> processor (p.lock ());
1959 if (!processor || !processor->display_to_user ()) {
1963 /* we use this override to veto the Amp processor from the plugin menu,
1964 as its automation lane can be accessed using the special "Fader" menu
1968 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1972 using namespace Menu_Helpers;
1973 ProcessorAutomationInfo *rai;
1974 list<ProcessorAutomationInfo*>::iterator x;
1976 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1978 if (automatable.empty()) {
1982 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1983 if ((*x)->processor == processor) {
1988 if (x == processor_automation.end()) {
1990 rai = new ProcessorAutomationInfo (processor);
1991 processor_automation.push_back (rai);
1999 /* any older menu was deleted at the top of processors_changed()
2000 when we cleared the subplugin menu.
2003 rai->menu = manage (new Menu);
2004 MenuList& items = rai->menu->items();
2005 rai->menu->set_name ("ArdourContextMenu");
2009 std::set<Evoral::Parameter> has_visible_automation;
2010 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2012 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2014 ProcessorAutomationNode* pan;
2015 CheckMenuItem* mitem;
2017 string name = processor->describe_parameter (*i);
2019 items.push_back (CheckMenuElem (name));
2020 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2022 _subplugin_menu_map[*i] = mitem;
2024 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2025 mitem->set_active(true);
2028 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2032 pan = new ProcessorAutomationNode (*i, mitem, *this);
2034 rai->lines.push_back (pan);
2038 pan->menu_item = mitem;
2042 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2045 /* add the menu for this processor, because the subplugin
2046 menu is always cleared at the top of processors_changed().
2047 this is the result of some poor design in gtkmm and/or
2051 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2056 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2057 RouteTimeAxisView::ProcessorAutomationNode* pan)
2059 bool showit = pan->menu_item->get_active();
2060 bool redraw = false;
2062 if (pan->view == 0 && showit) {
2063 add_processor_automation_curve (rai->processor, pan->what);
2067 if (pan->view && pan->view->set_marked_for_display (showit)) {
2071 if (redraw && !no_redraw) {
2077 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2079 if (c.type == RouteProcessorChange::MeterPointChange) {
2080 /* nothing to do if only the meter point has changed */
2084 using namespace Menu_Helpers;
2086 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2087 (*i)->valid = false;
2090 setup_processor_menu_and_curves ();
2092 bool deleted_processor_automation = false;
2094 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2096 list<ProcessorAutomationInfo*>::iterator tmp;
2104 processor_automation.erase (i);
2105 deleted_processor_automation = true;
2112 if (deleted_processor_automation && !no_redraw) {
2117 boost::shared_ptr<AutomationLine>
2118 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2120 ProcessorAutomationNode* pan;
2122 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2128 return boost::shared_ptr<AutomationLine>();
2132 RouteTimeAxisView::reset_processor_automation_curves ()
2134 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2140 RouteTimeAxisView::update_rec_display ()
2142 RouteUI::update_rec_display ();
2143 name_entry.set_sensitive (!_route->record_enabled());
2147 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2149 if (apply_to_selection) {
2150 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2154 _view->set_layer_display (d);
2157 set_gui_property (X_("layer-display"), enum_2_string (d));
2162 RouteTimeAxisView::layer_display () const
2165 return _view->layer_display ();
2168 /* we don't know, since we don't have a _view, so just return something */
2174 boost::shared_ptr<AutomationTimeAxisView>
2175 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2177 AutomationTracks::iterator i = _automation_tracks.find(param);
2178 if (i != _automation_tracks.end()) {
2181 return boost::shared_ptr<AutomationTimeAxisView>();
2186 RouteTimeAxisView::fast_update ()
2188 gm.get_level_meter().update_meters ();
2192 RouteTimeAxisView::hide_meter ()
2195 gm.get_level_meter().hide_meters ();
2199 RouteTimeAxisView::show_meter ()
2205 RouteTimeAxisView::reset_meter ()
2207 if (Config->get_show_track_meters()) {
2208 gm.get_level_meter().setup_meters (height-5);
2215 RouteTimeAxisView::clear_meter ()
2217 gm.get_level_meter().clear_meters ();
2221 RouteTimeAxisView::meter_changed ()
2223 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2228 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2234 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2236 using namespace Menu_Helpers;
2238 if (!_underlay_streams.empty()) {
2239 MenuList& parent_items = parent_menu->items();
2240 Menu* gs_menu = manage (new Menu);
2241 gs_menu->set_name ("ArdourContextMenu");
2242 MenuList& gs_items = gs_menu->items();
2244 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2246 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2247 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2248 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2254 RouteTimeAxisView::set_underlay_state()
2256 if (!underlay_xml_node) {
2260 XMLNodeList nlist = underlay_xml_node->children();
2261 XMLNodeConstIterator niter;
2262 XMLNode *child_node;
2264 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2265 child_node = *niter;
2267 if (child_node->name() != "Underlay") {
2271 XMLProperty* prop = child_node->property ("id");
2273 PBD::ID id (prop->value());
2275 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2278 add_underlay(v->view(), false);
2287 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2293 RouteTimeAxisView& other = v->trackview();
2295 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2296 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2297 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2301 _underlay_streams.push_back(v);
2302 other._underlay_mirrors.push_back(this);
2304 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2306 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2308 if (!underlay_xml_node) {
2309 underlay_xml_node = xml_node->add_child("Underlays");
2312 XMLNode* node = underlay_xml_node->add_child("Underlay");
2313 XMLProperty* prop = node->add_property("id");
2314 prop->set_value(v->trackview().route()->id().to_s());
2321 RouteTimeAxisView::remove_underlay (StreamView* v)
2327 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2328 RouteTimeAxisView& other = v->trackview();
2330 if (it != _underlay_streams.end()) {
2331 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2333 if (gm == other._underlay_mirrors.end()) {
2334 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2338 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2340 _underlay_streams.erase(it);
2341 other._underlay_mirrors.erase(gm);
2343 if (underlay_xml_node) {
2344 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2350 RouteTimeAxisView::set_button_names ()
2352 if (_route && _route->solo_safe()) {
2353 solo_button->remove ();
2354 if (solo_safe_pixbuf == 0) {
2355 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2357 solo_button->set_image (solo_safe_pixbuf);
2358 solo_button->set_text (string());
2360 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2361 if (Config->get_solo_control_is_listen_control()) {
2362 switch (Config->get_listen_position()) {
2363 case AfterFaderListen:
2364 solo_button->set_text (_("A"));
2365 ARDOUR_UI::instance()->set_tip (*solo_button, _("After-fade listen (AFL)"));
2367 case PreFaderListen:
2368 solo_button->set_text (_("P"));
2369 ARDOUR_UI::instance()->set_tip (*solo_button, _("Pre-fade listen (PFL)"));
2373 solo_button->set_text (_("s"));
2374 ARDOUR_UI::instance()->set_tip (*solo_button, _("Solo"));
2377 mute_button->set_text (_("m"));
2381 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2383 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2384 if (i != _main_automation_menu_map.end()) {
2388 i = _subplugin_menu_map.find (param);
2389 if (i != _subplugin_menu_map.end()) {
2397 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2399 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2401 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2405 gain_track.reset (new AutomationTimeAxisView (_session,
2406 _route, _route->amp(), c, param,
2411 _route->amp()->describe_parameter(param)));
2414 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2417 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2421 void add_region_to_list (RegionView* rv, RegionList* l)
2423 l->push_back (rv->region());
2427 RouteTimeAxisView::combine_regions ()
2429 /* as of may 2011, we do not offer uncombine for MIDI tracks
2432 if (!is_audio_track()) {
2440 RegionList selected_regions;
2441 boost::shared_ptr<Playlist> playlist = track()->playlist();
2443 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2445 if (selected_regions.size() < 2) {
2449 playlist->clear_changes ();
2450 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2452 _session->add_command (new StatefulDiffCommand (playlist));
2453 /* make the new region be selected */
2455 return _view->find_view (compound_region);
2459 RouteTimeAxisView::uncombine_regions ()
2461 /* as of may 2011, we do not offer uncombine for MIDI tracks
2463 if (!is_audio_track()) {
2471 RegionList selected_regions;
2472 boost::shared_ptr<Playlist> playlist = track()->playlist();
2474 /* have to grab selected regions first because the uncombine is going
2475 * to change that in the middle of the list traverse
2478 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2480 playlist->clear_changes ();
2482 for (RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2483 playlist->uncombine (*i);
2486 _session->add_command (new StatefulDiffCommand (playlist));
2490 RouteTimeAxisView::state_id() const
2492 return string_compose ("rtav %1", _route->id().to_s());
2497 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2499 TimeAxisView::remove_child (c);
2501 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2503 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2504 if (i->second == a) {
2505 _automation_tracks.erase (i);