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"
71 #include "crossfade_view.h"
73 #include "gui_thread.h"
75 #include "playlist_selector.h"
76 #include "point_selection.h"
78 #include "public_editor.h"
79 #include "region_view.h"
80 #include "rgb_macros.h"
81 #include "selection.h"
82 #include "simplerect.h"
83 #include "streamview.h"
85 #include "route_group_menu.h"
87 #include "ardour/track.h"
91 using namespace ARDOUR;
93 using namespace Gtkmm2ext;
95 using namespace Editing;
99 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
102 RouteTimeAxisView::setup_slider_pix ()
104 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
105 throw failed_constructor ();
109 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
112 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
114 , parent_canvas (canvas)
116 , button_table (3, 3)
117 , route_group_button (_("g"))
118 , playlist_button (_("p"))
119 , automation_button (_("a"))
120 , automation_action_menu (0)
121 , plugins_submenu_item (0)
122 , route_group_menu (0)
123 , playlist_action_menu (0)
125 , color_mode_menu (0)
126 , gm (sess, slider, true, 115)
131 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
133 RouteUI::set_route (rt);
135 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
136 gm.get_level_meter().set_no_show_all();
137 gm.get_level_meter().setup_meters(50);
139 string str = gui_property ("height");
141 set_height (atoi (str));
143 set_height (preset_height (HeightNormal));
146 if (!_route->is_hidden()) {
147 if (gui_property ("visible").empty()) {
148 set_gui_property ("visible", true);
151 set_gui_property ("visible", false);
155 update_solo_display ();
157 timestretch_rect = 0;
160 ignore_toggle = false;
162 route_group_button.set_name ("route button");
163 playlist_button.set_name ("route button");
164 automation_button.set_name ("route button");
166 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
167 playlist_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
168 automation_button.signal_clicked.connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
174 switch (track()->mode()) {
176 case ARDOUR::NonLayered:
177 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
179 case ARDOUR::Destructive:
180 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
184 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
186 if (is_midi_track()) {
187 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
189 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
192 rec_enable_button->set_sensitive (_session->writable());
195 controls_hbox.pack_start(gm.get_level_meter(), false, false);
196 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
197 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
198 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
200 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
202 if (!_route->is_master()) {
203 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
207 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
209 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
210 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
211 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
212 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
214 if (is_midi_track()) {
215 ARDOUR_UI::instance()->set_tip(automation_button, _("MIDI Controllers and Automation"));
217 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
222 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
224 if (is_track() && track()->mode() == ARDOUR::Normal) {
225 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
230 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
231 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
235 str = gui_property ("layer-display");
237 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
240 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
241 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
243 /* pick up the correct freeze state */
247 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
248 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
249 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
251 PropertyList* plist = new PropertyList();
253 plist->add (ARDOUR::Properties::edit, true);
254 plist->add (ARDOUR::Properties::mute, true);
255 plist->add (ARDOUR::Properties::solo, true);
257 route_group_menu = new RouteGroupMenu (_session, plist);
259 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
260 gm.get_gain_slider().set_name ("TrackGainFader");
262 gm.get_level_meter().signal_scroll_event().connect (sigc::mem_fun (*this, &RouteTimeAxisView::controls_ebox_scroll), false);
268 RouteTimeAxisView::~RouteTimeAxisView ()
270 CatchDeletion (this);
272 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
276 delete playlist_action_menu;
277 playlist_action_menu = 0;
282 _automation_tracks.clear ();
284 delete route_group_menu;
288 RouteTimeAxisView::post_construct ()
290 /* map current state of the route */
292 update_diskstream_display ();
293 setup_processor_menu_and_curves ();
294 reset_processor_automation_curves ();
297 /** Set up the processor menu for the current set of processors, and
298 * display automation curves for any parameters which have data.
301 RouteTimeAxisView::setup_processor_menu_and_curves ()
303 _subplugin_menu_map.clear ();
304 subplugin_menu.items().clear ();
305 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
306 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
310 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
312 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
313 if (_route->route_group()) {
314 _route->route_group()->remove (_route);
320 r.push_back (route ());
322 route_group_menu->build (r);
323 route_group_menu->menu()->popup (ev->button, ev->time);
329 RouteTimeAxisView::playlist_changed ()
335 RouteTimeAxisView::label_view ()
337 string x = _route->name();
339 if (x != name_entry.get_text()) {
340 name_entry.set_text (x);
343 if (x != name_label.get_text()) {
344 name_label.set_text (x);
347 ARDOUR_UI::instance()->set_tip (name_entry, x);
351 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
353 if (what_changed.contains (ARDOUR::Properties::name)) {
359 RouteTimeAxisView::take_name_changed (void *src)
367 RouteTimeAxisView::playlist_click ()
369 build_playlist_menu ();
370 conditionally_add_to_selection ();
371 playlist_action_menu->popup (1, gtk_get_current_event_time());
375 RouteTimeAxisView::automation_click ()
377 conditionally_add_to_selection ();
378 build_automation_action_menu (false);
379 automation_action_menu->popup (1, gtk_get_current_event_time());
383 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
385 using namespace Menu_Helpers;
387 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
388 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
391 detach_menu (subplugin_menu);
393 _main_automation_menu_map.clear ();
394 delete automation_action_menu;
395 automation_action_menu = new Menu;
397 MenuList& items = automation_action_menu->items();
399 automation_action_menu->set_name ("ArdourContextMenu");
401 items.push_back (MenuElem (_("Show All Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
404 items.push_back (MenuElem (_("Show Existing Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
407 items.push_back (MenuElem (_("Hide All Automation"),
408 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
410 items.push_back (SeparatorElem ());
412 /* Attach the plugin submenu. It may have previously been used elsewhere,
413 so it was detached above */
415 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
416 items.back().set_sensitive (!subplugin_menu.items().empty() && (!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 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
489 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
490 i->set_active (overlaid != 0 && stacked == 0);
491 i->set_inconsistent (overlaid != 0 && stacked != 0);
492 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
494 layers_items.push_back (
495 RadioMenuElem (layers_group, _("Stacked"),
496 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
499 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
500 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
501 i->set_active (overlaid == 0 && stacked != 0);
502 i->set_inconsistent (overlaid != 0 && stacked != 0);
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));
659 color_mode_menu = build_color_mode_menu();
660 if (color_mode_menu) {
661 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
664 items.push_back (SeparatorElem());
666 build_playlist_menu ();
667 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
668 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
670 route_group_menu->detach ();
673 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
674 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
676 r.push_back (rtv->route ());
681 r.push_back (route ());
684 route_group_menu->build (r);
685 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
687 build_automation_action_menu (true);
688 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
690 items.push_back (SeparatorElem());
695 TrackSelection const & s = _editor.get_selection().tracks;
696 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
697 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
702 if (r->route()->active()) {
709 items.push_back (CheckMenuElem (_("Active")));
710 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
711 bool click_sets_active = true;
712 if (active > 0 && inactive == 0) {
713 i->set_active (true);
714 click_sets_active = false;
715 } else if (active > 0 && inactive > 0) {
716 i->set_inconsistent (true);
718 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
720 items.push_back (SeparatorElem());
721 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
722 if (!Profile->get_sae()) {
723 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
725 items.push_front (SeparatorElem());
726 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
731 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
733 if (apply_to_selection) {
734 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
739 if (!track()->can_use_mode (mode, needs_bounce)) {
745 cerr << "would bounce this one\n";
750 track()->set_mode (mode);
752 rec_enable_button->remove ();
755 case ARDOUR::NonLayered:
757 rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
758 rec_enable_button->set_text (string());
760 case ARDOUR::Destructive:
761 rec_enable_button->set_image (::get_icon (X_("record_tape_red")));
762 rec_enable_button->set_text (string());
766 rec_enable_button->show_all ();
771 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
777 TimeAxisView::show_timestretch (start, end);
787 /* check that the time selection was made in our route, or our route group.
788 remember that route_group() == 0 implies the route is *not* in a edit group.
791 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
792 /* this doesn't apply to us */
796 /* ignore it if our edit group is not active */
798 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
803 if (timestretch_rect == 0) {
804 timestretch_rect = new SimpleRect (*canvas_display ());
805 timestretch_rect->property_x1() = 0.0;
806 timestretch_rect->property_y1() = 0.0;
807 timestretch_rect->property_x2() = 0.0;
808 timestretch_rect->property_y2() = 0.0;
809 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
810 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
813 timestretch_rect->show ();
814 timestretch_rect->raise_to_top ();
816 x1 = start / _editor.get_current_zoom();
817 x2 = (end - 1) / _editor.get_current_zoom();
818 y2 = current_height() - 2;
820 timestretch_rect->property_x1() = x1;
821 timestretch_rect->property_y1() = 1.0;
822 timestretch_rect->property_x2() = x2;
823 timestretch_rect->property_y2() = y2;
827 RouteTimeAxisView::hide_timestretch ()
829 TimeAxisView::hide_timestretch ();
831 if (timestretch_rect) {
832 timestretch_rect->hide ();
837 RouteTimeAxisView::show_selection (TimeSelection& ts)
841 /* ignore it if our edit group is not active or if the selection was started
842 in some other track or route group (remember that route_group() == 0 means
843 that the track is not in an route group).
846 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
847 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
853 TimeAxisView::show_selection (ts);
857 RouteTimeAxisView::set_height (uint32_t h)
860 bool height_changed = (height == 0) || (h != height);
861 gm.get_level_meter().setup_meters (gmlen);
863 TimeAxisView::set_height (h);
866 _view->set_height ((double) current_height());
869 if (height >= preset_height (HeightNormal)) {
873 gm.get_gain_slider().show();
875 if (!_route || _route->is_monitor()) {
880 if (rec_enable_button)
881 rec_enable_button->show();
883 route_group_button.show();
884 automation_button.show();
886 if (is_track() && track()->mode() == ARDOUR::Normal) {
887 playlist_button.show();
894 gm.get_gain_slider().hide();
896 if (!_route || _route->is_monitor()) {
901 if (rec_enable_button)
902 rec_enable_button->show();
904 route_group_button.hide ();
905 automation_button.hide ();
907 if (is_track() && track()->mode() == ARDOUR::Normal) {
908 playlist_button.hide ();
913 if (height_changed && !no_redraw) {
914 /* only emit the signal if the height really changed */
920 RouteTimeAxisView::route_color_changed ()
923 _view->apply_color (color(), StreamView::RegionColor);
928 RouteTimeAxisView::reset_samples_per_unit ()
930 set_samples_per_unit (_editor.get_current_zoom());
934 RouteTimeAxisView::horizontal_position_changed ()
937 _view->horizontal_position_changed ();
942 RouteTimeAxisView::set_samples_per_unit (double spu)
947 speed = track()->speed();
951 _view->set_samples_per_unit (spu * speed);
954 TimeAxisView::set_samples_per_unit (spu * speed);
958 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
960 if (!mitem->get_active()) {
961 /* this is one of the two calls made when these radio menu items change status. this one
962 is for the item that became inactive, and we want to ignore it.
967 if (apply_to_selection) {
968 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
971 track()->set_align_choice (choice);
977 RouteTimeAxisView::rename_current_playlist ()
979 ArdourPrompter prompter (true);
982 boost::shared_ptr<Track> tr = track();
983 if (!tr || tr->destructive()) {
987 boost::shared_ptr<Playlist> pl = tr->playlist();
992 prompter.set_title (_("Rename Playlist"));
993 prompter.set_prompt (_("New name for playlist:"));
994 prompter.set_initial_text (pl->name());
995 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
996 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
998 switch (prompter.run ()) {
999 case Gtk::RESPONSE_ACCEPT:
1000 prompter.get_result (name);
1001 if (name.length()) {
1002 pl->set_name (name);
1012 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1014 std::string ret (basename);
1016 std::string const group_string = "." + route_group()->name() + ".";
1018 // iterate through all playlists
1020 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1021 std::string tmp = (*i)->name();
1023 std::string::size_type idx = tmp.find(group_string);
1024 // find those which belong to this group
1025 if (idx != string::npos) {
1026 tmp = tmp.substr(idx + group_string.length());
1028 // and find the largest current number
1029 int x = atoi(tmp.c_str());
1030 if (x > maxnumber) {
1039 snprintf (buf, sizeof(buf), "%d", maxnumber);
1041 ret = this->name() + "." + route_group()->name () + "." + buf;
1047 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1051 boost::shared_ptr<Track> tr = track ();
1052 if (!tr || tr->destructive()) {
1056 boost::shared_ptr<const Playlist> pl = tr->playlist();
1063 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1064 name = resolve_new_group_playlist_name(name, playlists_before_op);
1067 while (_session->playlists->by_name(name)) {
1068 name = Playlist::bump_name (name, *_session);
1071 // TODO: The prompter "new" button should be de-activated if the user
1072 // specifies a playlist name which already exists in the session.
1076 ArdourPrompter prompter (true);
1078 prompter.set_title (_("New Copy Playlist"));
1079 prompter.set_prompt (_("Name for new playlist:"));
1080 prompter.set_initial_text (name);
1081 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1082 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1083 prompter.show_all ();
1085 switch (prompter.run ()) {
1086 case Gtk::RESPONSE_ACCEPT:
1087 prompter.get_result (name);
1095 if (name.length()) {
1096 tr->use_copy_playlist ();
1097 tr->playlist()->set_name (name);
1102 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1106 boost::shared_ptr<Track> tr = track ();
1107 if (!tr || tr->destructive()) {
1111 boost::shared_ptr<const Playlist> pl = tr->playlist();
1118 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1119 name = resolve_new_group_playlist_name(name,playlists_before_op);
1122 while (_session->playlists->by_name(name)) {
1123 name = Playlist::bump_name (name, *_session);
1129 ArdourPrompter prompter (true);
1131 prompter.set_title (_("New Playlist"));
1132 prompter.set_prompt (_("Name for new playlist:"));
1133 prompter.set_initial_text (name);
1134 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1135 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1137 switch (prompter.run ()) {
1138 case Gtk::RESPONSE_ACCEPT:
1139 prompter.get_result (name);
1147 if (name.length()) {
1148 tr->use_new_playlist ();
1149 tr->playlist()->set_name (name);
1154 RouteTimeAxisView::clear_playlist ()
1156 boost::shared_ptr<Track> tr = track ();
1157 if (!tr || tr->destructive()) {
1161 boost::shared_ptr<Playlist> pl = tr->playlist();
1166 _editor.clear_playlist (pl);
1170 RouteTimeAxisView::speed_changed ()
1172 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1176 RouteTimeAxisView::update_diskstream_display ()
1186 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1188 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1190 /* special case: select/deselect all tracks */
1191 if (_editor.get_selection().selected (this)) {
1192 _editor.get_selection().clear_tracks ();
1194 _editor.select_all_tracks ();
1200 switch (ArdourKeyboard::selection_type (ev->state)) {
1201 case Selection::Toggle:
1202 _editor.get_selection().toggle (this);
1205 case Selection::Set:
1206 _editor.get_selection().set (this);
1209 case Selection::Extend:
1210 _editor.extend_selection_to_track (*this);
1213 case Selection::Add:
1214 _editor.get_selection().add (this);
1220 RouteTimeAxisView::set_selected_points (PointSelection& points)
1222 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1223 (*i)->set_selected_points (points);
1228 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1231 _view->set_selected_regionviews (regions);
1235 /** Add the selectable things that we have to a list.
1236 * @param results List to add things to.
1239 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1244 speed = track()->speed();
1247 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1248 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1250 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1251 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1254 /* pick up visible automation tracks */
1256 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1257 if (!(*i)->hidden()) {
1258 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1264 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1267 _view->get_inverted_selectables (sel, results);
1270 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1271 if (!(*i)->hidden()) {
1272 (*i)->get_inverted_selectables (sel, results);
1280 RouteTimeAxisView::route_group () const
1282 return _route->route_group();
1286 RouteTimeAxisView::name() const
1288 return _route->name();
1291 boost::shared_ptr<Playlist>
1292 RouteTimeAxisView::playlist () const
1294 boost::shared_ptr<Track> tr;
1296 if ((tr = track()) != 0) {
1297 return tr->playlist();
1299 return boost::shared_ptr<Playlist> ();
1304 RouteTimeAxisView::name_entry_changed ()
1306 string x = name_entry.get_text ();
1308 if (x == _route->name()) {
1312 strip_whitespace_edges (x);
1314 if (x.length() == 0) {
1315 name_entry.set_text (_route->name());
1319 if (_session->route_name_internal (x)) {
1320 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1322 name_entry.grab_focus ();
1323 } else if (RouteUI::verify_new_route_name (x)) {
1324 _route->set_name (x);
1326 name_entry.grab_focus ();
1330 boost::shared_ptr<Region>
1331 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1333 boost::shared_ptr<Playlist> pl = playlist ();
1336 return pl->find_next_region (pos, point, dir);
1339 return boost::shared_ptr<Region> ();
1343 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1345 boost::shared_ptr<Playlist> pl = playlist ();
1348 return pl->find_next_region_boundary (pos, dir);
1355 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1357 boost::shared_ptr<Playlist> what_we_got;
1358 boost::shared_ptr<Track> tr = track ();
1359 boost::shared_ptr<Playlist> playlist;
1362 /* route is a bus, not a track */
1366 playlist = tr->playlist();
1368 TimeSelection time (selection.time);
1369 float const speed = tr->speed();
1370 if (speed != 1.0f) {
1371 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1372 (*i).start = session_frame_to_track_frame((*i).start, speed);
1373 (*i).end = session_frame_to_track_frame((*i).end, speed);
1377 playlist->clear_changes ();
1378 playlist->clear_owned_changes ();
1382 if (playlist->cut (time) != 0) {
1383 vector<Command*> cmds;
1384 playlist->rdiff (cmds);
1385 _session->add_commands (cmds);
1387 _session->add_command (new StatefulDiffCommand (playlist));
1392 if ((what_we_got = playlist->cut (time)) != 0) {
1393 _editor.get_cut_buffer().add (what_we_got);
1394 vector<Command*> cmds;
1395 playlist->rdiff (cmds);
1396 _session->add_commands (cmds);
1398 _session->add_command (new StatefulDiffCommand (playlist));
1402 if ((what_we_got = playlist->copy (time)) != 0) {
1403 _editor.get_cut_buffer().add (what_we_got);
1408 if ((what_we_got = playlist->cut (time)) != 0) {
1410 vector<Command*> cmds;
1411 playlist->rdiff (cmds);
1412 _session->add_commands (cmds);
1413 _session->add_command (new StatefulDiffCommand (playlist));
1414 what_we_got->release ();
1421 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1427 boost::shared_ptr<Playlist> pl = playlist ();
1428 PlaylistSelection::iterator p;
1430 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1432 if (p == selection.playlists.end()) {
1436 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1438 if (track()->speed() != 1.0f) {
1439 pos = session_frame_to_track_frame (pos, track()->speed());
1440 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1443 pl->clear_changes ();
1444 pl->paste (*p, pos, times);
1445 _session->add_command (new StatefulDiffCommand (pl));
1451 struct PlaylistSorter {
1452 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1453 return a->sort_id() < b->sort_id();
1458 RouteTimeAxisView::build_playlist_menu ()
1460 using namespace Menu_Helpers;
1466 delete playlist_action_menu;
1467 playlist_action_menu = new Menu;
1468 playlist_action_menu->set_name ("ArdourContextMenu");
1470 MenuList& playlist_items = playlist_action_menu->items();
1471 playlist_action_menu->set_name ("ArdourContextMenu");
1472 playlist_items.clear();
1474 RadioMenuItem::Group playlist_group;
1475 boost::shared_ptr<Track> tr = track ();
1477 vector<boost::shared_ptr<Playlist> > playlists_tr = _session->playlists->playlists_for_track (tr);
1479 /* sort the playlists */
1481 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1483 /* add the playlists to the menu */
1484 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1485 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1486 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1487 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1489 if (tr->playlist()->id() == (*i)->id()) {
1495 playlist_items.push_back (SeparatorElem());
1496 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1497 playlist_items.push_back (SeparatorElem());
1499 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1500 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1501 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1504 // Use a label which tells the user what is happening
1505 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1506 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1510 playlist_items.push_back (SeparatorElem());
1511 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1512 playlist_items.push_back (SeparatorElem());
1514 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1518 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1520 assert (is_track());
1522 // exit if we were triggered by deactivating the old playlist
1523 if (!item->get_active()) {
1527 boost::shared_ptr<Playlist> pl (wpl.lock());
1533 if (track()->playlist() == pl) {
1534 // exit when use_playlist is called by the creation of the playlist menu
1535 // or the playlist choice is unchanged
1539 track()->use_playlist (pl);
1541 RouteGroup* rg = route_group();
1543 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1544 std::string group_string = "." + rg->name() + ".";
1546 std::string take_name = pl->name();
1547 std::string::size_type idx = take_name.find(group_string);
1549 if (idx == std::string::npos)
1552 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1554 boost::shared_ptr<RouteList> rl (rg->route_list());
1556 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1557 if ( (*i) == this->route()) {
1561 std::string playlist_name = (*i)->name()+group_string+take_name;
1563 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1568 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1570 // No playlist for this track for this take yet, make it
1571 track->use_new_playlist();
1572 track->playlist()->set_name(playlist_name);
1574 track->use_playlist(ipl);
1581 RouteTimeAxisView::show_playlist_selector ()
1583 _editor.playlist_selector().show_for (this);
1587 RouteTimeAxisView::map_frozen ()
1593 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1595 switch (track()->freeze_state()) {
1597 playlist_button.set_sensitive (false);
1598 rec_enable_button->set_sensitive (false);
1601 playlist_button.set_sensitive (true);
1602 rec_enable_button->set_sensitive (true);
1608 RouteTimeAxisView::color_handler ()
1610 //case cTimeStretchOutline:
1611 if (timestretch_rect) {
1612 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1614 //case cTimeStretchFill:
1615 if (timestretch_rect) {
1616 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1622 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1623 * Will add track if necessary.
1626 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1628 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1629 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1632 /* it doesn't exist yet, so we don't care about the button state: just add it */
1633 create_automation_child (param, true);
1636 bool yn = menu->get_active();
1637 bool changed = false;
1639 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1641 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1642 will have done that for us.
1645 if (changed && !no_redraw) {
1653 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1655 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1661 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1663 if (menu && !_hidden) {
1664 ignore_toggle = true;
1665 menu->set_active (false);
1666 ignore_toggle = false;
1669 if (_route && !no_redraw) {
1676 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1678 if (apply_to_selection) {
1679 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1683 /* Show our automation */
1685 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1686 i->second->set_marked_for_display (true);
1688 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1691 menu->set_active(true);
1696 /* Show processor automation */
1698 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1699 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1700 if ((*ii)->view == 0) {
1701 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1704 (*ii)->menu_item->set_active (true);
1717 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1719 if (apply_to_selection) {
1720 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1724 /* Show our automation */
1726 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1727 if (i->second->has_automation()) {
1728 i->second->set_marked_for_display (true);
1730 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1732 menu->set_active(true);
1737 /* Show processor automation */
1739 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1740 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1741 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1742 (*ii)->menu_item->set_active (true);
1754 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1756 if (apply_to_selection) {
1757 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1761 /* Hide our automation */
1763 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1764 i->second->set_marked_for_display (false);
1766 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1769 menu->set_active (false);
1773 /* Hide processor automation */
1775 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1776 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1777 (*ii)->menu_item->set_active (false);
1788 RouteTimeAxisView::region_view_added (RegionView* rv)
1790 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1791 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1792 boost::shared_ptr<AutomationTimeAxisView> atv;
1794 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1799 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1800 (*i)->add_ghost(rv);
1804 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1806 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1812 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1814 parent.remove_processor_automation_node (this);
1818 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1821 remove_child (pan->view);
1825 RouteTimeAxisView::ProcessorAutomationNode*
1826 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1828 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1830 if ((*i)->processor == processor) {
1832 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1833 if ((*ii)->what == what) {
1843 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1845 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1848 ProcessorAutomationNode* pan;
1850 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1851 /* session state may never have been saved with new plugin */
1852 error << _("programming error: ")
1853 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1854 processor->name(), what.type(), (int) what.channel(), what.id() )
1864 boost::shared_ptr<AutomationControl> control
1865 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1867 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1868 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1869 _editor, *this, false, parent_canvas,
1870 processor->describe_parameter (what), processor->name()));
1872 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1874 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1877 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1882 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor>)
1885 pan->menu_item->set_active (false);
1894 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1896 boost::shared_ptr<Processor> processor (p.lock ());
1898 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1899 /* The Amp processor is a special case and is dealt with separately */
1903 set<Evoral::Parameter> existing;
1905 processor->what_has_data (existing);
1907 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1909 Evoral::Parameter param (*i);
1910 boost::shared_ptr<AutomationLine> al;
1912 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1915 add_processor_automation_curve (processor, param);
1921 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1923 using namespace Menu_Helpers;
1927 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1929 _automation_tracks[param] = track;
1931 /* existing state overrides "show" argument */
1932 string s = track->gui_property ("visible");
1934 show = string_is_affirmative (s);
1937 /* this might or might not change the visibility status, so don't rely on it */
1938 track->set_marked_for_display (show);
1940 if (show && !no_redraw) {
1944 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1945 /* MIDI-related parameters are always in the menu, there's no
1946 reason to rebuild the menu just because we added a automation
1947 lane for one of them. But if we add a non-MIDI automation
1948 lane, then we need to invalidate the display menu.
1950 delete display_menu;
1956 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1958 boost::shared_ptr<Processor> processor (p.lock ());
1960 if (!processor || !processor->display_to_user ()) {
1964 /* we use this override to veto the Amp processor from the plugin menu,
1965 as its automation lane can be accessed using the special "Fader" menu
1969 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1973 using namespace Menu_Helpers;
1974 ProcessorAutomationInfo *rai;
1975 list<ProcessorAutomationInfo*>::iterator x;
1977 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1979 if (automatable.empty()) {
1983 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1984 if ((*x)->processor == processor) {
1989 if (x == processor_automation.end()) {
1991 rai = new ProcessorAutomationInfo (processor);
1992 processor_automation.push_back (rai);
2000 /* any older menu was deleted at the top of processors_changed()
2001 when we cleared the subplugin menu.
2004 rai->menu = manage (new Menu);
2005 MenuList& items = rai->menu->items();
2006 rai->menu->set_name ("ArdourContextMenu");
2010 std::set<Evoral::Parameter> has_visible_automation;
2011 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2013 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2015 ProcessorAutomationNode* pan;
2016 CheckMenuItem* mitem;
2018 string name = processor->describe_parameter (*i);
2020 items.push_back (CheckMenuElem (name));
2021 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2023 _subplugin_menu_map[*i] = mitem;
2025 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2026 mitem->set_active(true);
2029 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2033 pan = new ProcessorAutomationNode (*i, mitem, *this);
2035 rai->lines.push_back (pan);
2039 pan->menu_item = mitem;
2043 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2046 /* add the menu for this processor, because the subplugin
2047 menu is always cleared at the top of processors_changed().
2048 this is the result of some poor design in gtkmm and/or
2052 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2057 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2058 RouteTimeAxisView::ProcessorAutomationNode* pan)
2060 bool showit = pan->menu_item->get_active();
2061 bool redraw = false;
2063 if (pan->view == 0 && showit) {
2064 add_processor_automation_curve (rai->processor, pan->what);
2068 if (pan->view && pan->view->set_marked_for_display (showit)) {
2072 if (redraw && !no_redraw) {
2078 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2080 if (c.type == RouteProcessorChange::MeterPointChange) {
2081 /* nothing to do if only the meter point has changed */
2085 using namespace Menu_Helpers;
2087 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2088 (*i)->valid = false;
2091 setup_processor_menu_and_curves ();
2093 bool deleted_processor_automation = false;
2095 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2097 list<ProcessorAutomationInfo*>::iterator tmp;
2105 processor_automation.erase (i);
2106 deleted_processor_automation = true;
2113 if (deleted_processor_automation && !no_redraw) {
2118 boost::shared_ptr<AutomationLine>
2119 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2121 ProcessorAutomationNode* pan;
2123 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2129 return boost::shared_ptr<AutomationLine>();
2133 RouteTimeAxisView::reset_processor_automation_curves ()
2135 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2141 RouteTimeAxisView::update_rec_display ()
2143 RouteUI::update_rec_display ();
2144 name_entry.set_sensitive (!_route->record_enabled());
2148 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2150 if (apply_to_selection) {
2151 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2155 _view->set_layer_display (d);
2158 set_gui_property (X_("layer-display"), enum_2_string (d));
2163 RouteTimeAxisView::layer_display () const
2166 return _view->layer_display ();
2169 /* we don't know, since we don't have a _view, so just return something */
2175 boost::shared_ptr<AutomationTimeAxisView>
2176 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2178 AutomationTracks::iterator i = _automation_tracks.find(param);
2179 if (i != _automation_tracks.end()) {
2182 return boost::shared_ptr<AutomationTimeAxisView>();
2187 RouteTimeAxisView::fast_update ()
2189 gm.get_level_meter().update_meters ();
2193 RouteTimeAxisView::hide_meter ()
2196 gm.get_level_meter().hide_meters ();
2200 RouteTimeAxisView::show_meter ()
2206 RouteTimeAxisView::reset_meter ()
2208 if (Config->get_show_track_meters()) {
2209 gm.get_level_meter().setup_meters (height-5);
2216 RouteTimeAxisView::clear_meter ()
2218 gm.get_level_meter().clear_meters ();
2222 RouteTimeAxisView::meter_changed ()
2224 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2229 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2235 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2237 using namespace Menu_Helpers;
2239 if (!_underlay_streams.empty()) {
2240 MenuList& parent_items = parent_menu->items();
2241 Menu* gs_menu = manage (new Menu);
2242 gs_menu->set_name ("ArdourContextMenu");
2243 MenuList& gs_items = gs_menu->items();
2245 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2247 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2248 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2249 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2255 RouteTimeAxisView::set_underlay_state()
2257 if (!underlay_xml_node) {
2261 XMLNodeList nlist = underlay_xml_node->children();
2262 XMLNodeConstIterator niter;
2263 XMLNode *child_node;
2265 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2266 child_node = *niter;
2268 if (child_node->name() != "Underlay") {
2272 XMLProperty* prop = child_node->property ("id");
2274 PBD::ID id (prop->value());
2276 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2279 add_underlay(v->view(), false);
2288 RouteTimeAxisView::add_underlay (StreamView* v, bool /*update_xml*/)
2294 RouteTimeAxisView& other = v->trackview();
2296 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2297 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2298 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2302 _underlay_streams.push_back(v);
2303 other._underlay_mirrors.push_back(this);
2305 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2307 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2309 if (!underlay_xml_node) {
2310 underlay_xml_node = xml_node->add_child("Underlays");
2313 XMLNode* node = underlay_xml_node->add_child("Underlay");
2314 XMLProperty* prop = node->add_property("id");
2315 prop->set_value(v->trackview().route()->id().to_s());
2322 RouteTimeAxisView::remove_underlay (StreamView* v)
2328 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2329 RouteTimeAxisView& other = v->trackview();
2331 if (it != _underlay_streams.end()) {
2332 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2334 if (gm == other._underlay_mirrors.end()) {
2335 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2339 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2341 _underlay_streams.erase(it);
2342 other._underlay_mirrors.erase(gm);
2344 if (underlay_xml_node) {
2345 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2351 RouteTimeAxisView::set_button_names ()
2353 if (_route && _route->solo_safe()) {
2354 solo_button->remove ();
2355 if (solo_safe_pixbuf == 0) {
2356 solo_safe_pixbuf = ::get_icon("solo-safe-icon");
2358 solo_button->set_image (solo_safe_pixbuf);
2359 solo_button->set_text (string());
2361 solo_button->set_image (Glib::RefPtr<Gdk::Pixbuf>());
2362 if (Config->get_solo_control_is_listen_control()) {
2363 switch (Config->get_listen_position()) {
2364 case AfterFaderListen:
2365 solo_button->set_text (_("A"));
2367 case PreFaderListen:
2368 solo_button->set_text (_("P"));
2372 solo_button->set_text (_("s"));
2375 mute_button->set_text (_("m"));
2379 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2381 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2382 if (i != _main_automation_menu_map.end()) {
2386 i = _subplugin_menu_map.find (param);
2387 if (i != _subplugin_menu_map.end()) {
2395 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2397 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2399 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2403 gain_track.reset (new AutomationTimeAxisView (_session,
2404 _route, _route->amp(), c, param,
2409 _route->amp()->describe_parameter(param)));
2412 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2415 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2419 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2421 l->push_back (rv->region());
2425 RouteTimeAxisView::combine_regions ()
2427 /* as of may 2011, we do not offer uncombine for MIDI tracks
2430 if (!is_audio_track()) {
2438 Playlist::RegionList selected_regions;
2439 boost::shared_ptr<Playlist> playlist = track()->playlist();
2441 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2443 if (selected_regions.size() < 2) {
2447 playlist->clear_changes ();
2448 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2450 _session->add_command (new StatefulDiffCommand (playlist));
2451 /* make the new region be selected */
2453 return _view->find_view (compound_region);
2457 RouteTimeAxisView::uncombine_regions ()
2459 /* as of may 2011, we do not offer uncombine for MIDI tracks
2461 if (!is_audio_track()) {
2469 Playlist::RegionList selected_regions;
2470 boost::shared_ptr<Playlist> playlist = track()->playlist();
2472 /* have to grab selected regions first because the uncombine is going
2473 * to change that in the middle of the list traverse
2476 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2478 playlist->clear_changes ();
2480 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2481 playlist->uncombine (*i);
2484 _session->add_command (new StatefulDiffCommand (playlist));
2488 RouteTimeAxisView::state_id() const
2490 return string_compose ("rtav %1", _route->id().to_s());
2495 RouteTimeAxisView::remove_child (boost::shared_ptr<TimeAxisView> c)
2497 TimeAxisView::remove_child (c);
2499 boost::shared_ptr<AutomationTimeAxisView> a = boost::dynamic_pointer_cast<AutomationTimeAxisView> (c);
2501 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
2502 if (i->second == a) {
2503 _automation_tracks.erase (i);