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"
66 #include "global_signals.h"
67 #include "route_time_axis.h"
68 #include "automation_time_axis.h"
69 #include "canvas_impl.h"
70 #include "crossfade_view.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;
97 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
100 RouteTimeAxisView::setup_slider_pix ()
102 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
103 throw failed_constructor ();
107 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
110 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
112 , parent_canvas (canvas)
113 , button_table (3, 3)
114 , route_group_button (_("g"))
115 , playlist_button (_("p"))
116 , automation_button (_("a"))
117 , automation_action_menu (0)
118 , plugins_submenu_item (0)
119 , route_group_menu (0)
120 , playlist_action_menu (0)
122 , color_mode_menu (0)
123 , gm (sess, slider, true, 115)
128 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
130 RouteUI::set_route (rt);
132 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133 gm.get_level_meter().set_no_show_all();
134 gm.get_level_meter().setup_meters(50);
136 string str = gui_property ("height");
138 set_height (atoi (str));
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_hidden()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
148 set_gui_property ("visible", false);
152 update_solo_display ();
154 timestretch_rect = 0;
157 ignore_toggle = false;
159 route_group_button.set_name ("TrackGroupButton");
160 playlist_button.set_name ("TrackPlaylistButton");
161 automation_button.set_name ("TrackAutomationButton");
163 route_group_button.unset_flags (Gtk::CAN_FOCUS);
164 playlist_button.unset_flags (Gtk::CAN_FOCUS);
165 automation_button.unset_flags (Gtk::CAN_FOCUS);
167 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
168 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
169 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
175 rec_enable_button->remove ();
177 switch (track()->mode()) {
179 case ARDOUR::NonLayered:
180 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
182 case ARDOUR::Destructive:
183 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
186 rec_enable_button->show_all ();
188 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
190 if (is_midi_track()) {
191 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
193 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
196 rec_enable_button->set_sensitive (_session->writable());
199 controls_hbox.pack_start(gm.get_level_meter(), false, false);
200 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
201 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
202 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 if (!_route->is_master()) {
207 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
210 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
211 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
214 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
215 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
216 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
217 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), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
230 _route->PropertyChanged.connect (*this, invalidator (*this), ui_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");
265 RouteTimeAxisView::~RouteTimeAxisView ()
267 CatchDeletion (this);
269 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
273 delete playlist_action_menu;
274 playlist_action_menu = 0;
279 _automation_tracks.clear ();
281 delete route_group_menu;
285 RouteTimeAxisView::post_construct ()
287 /* map current state of the route */
289 update_diskstream_display ();
290 setup_processor_menu_and_curves ();
291 reset_processor_automation_curves ();
294 /** Set up the processor menu for the current set of processors, and
295 * display automation curves for any parameters which have data.
298 RouteTimeAxisView::setup_processor_menu_and_curves ()
300 _subplugin_menu_map.clear ();
301 subplugin_menu.items().clear ();
302 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
303 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
307 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
309 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
310 if (_route->route_group()) {
311 _route->route_group()->remove (_route);
317 r.push_back (route ());
319 route_group_menu->build (r);
320 route_group_menu->menu()->popup (ev->button, ev->time);
326 RouteTimeAxisView::playlist_changed ()
332 RouteTimeAxisView::label_view ()
334 string x = _route->name();
336 if (x != name_entry.get_text()) {
337 name_entry.set_text (x);
340 if (x != name_label.get_text()) {
341 name_label.set_text (x);
344 ARDOUR_UI::instance()->set_tip (name_entry, x);
348 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
350 if (what_changed.contains (ARDOUR::Properties::name)) {
356 RouteTimeAxisView::take_name_changed (void *src)
364 RouteTimeAxisView::playlist_click ()
366 build_playlist_menu ();
367 conditionally_add_to_selection ();
368 playlist_action_menu->popup (1, gtk_get_current_event_time());
372 RouteTimeAxisView::automation_click ()
374 conditionally_add_to_selection ();
375 build_automation_action_menu (false);
376 automation_action_menu->popup (1, gtk_get_current_event_time());
380 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
382 using namespace Menu_Helpers;
384 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
385 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
388 detach_menu (subplugin_menu);
390 _main_automation_menu_map.clear ();
391 delete automation_action_menu;
392 automation_action_menu = new Menu;
394 MenuList& items = automation_action_menu->items();
396 automation_action_menu->set_name ("ArdourContextMenu");
398 items.push_back (MenuElem (_("Show All Automation"),
399 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
401 items.push_back (MenuElem (_("Show Existing Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
404 items.push_back (MenuElem (_("Hide All Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
407 items.push_back (SeparatorElem ());
409 /* Attach the plugin submenu. It may have previously been used elsewhere,
410 so it was detached above */
412 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
413 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
417 RouteTimeAxisView::build_display_menu ()
419 using namespace Menu_Helpers;
423 TimeAxisView::build_display_menu ();
425 /* now fill it with our stuff */
427 MenuList& items = display_menu->items();
428 display_menu->set_name ("ArdourContextMenu");
430 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
433 detach_menu (*_size_menu);
436 items.push_back (MenuElem (_("Height"), *_size_menu));
438 items.push_back (SeparatorElem());
440 if (!Profile->get_sae()) {
441 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
442 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
443 items.push_back (SeparatorElem());
446 // Hook for derived classes to add type specific stuff
447 append_extra_display_menu_items ();
451 Menu* layers_menu = manage (new Menu);
452 MenuList &layers_items = layers_menu->items();
453 layers_menu->set_name("ArdourContextMenu");
455 RadioMenuItem::Group layers_group;
457 /* Find out how many overlaid/stacked tracks we have in the selection */
461 TrackSelection const & s = _editor.get_selection().tracks;
462 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
463 StreamView* v = (*i)->view ();
468 switch (v->layer_display ()) {
478 /* We're not connecting to signal_toggled() here; in the case where these two items are
479 set to be in the `inconsistent' state, it seems that one or other will end up active
480 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
481 select the active one, no toggled signal is emitted so nothing happens.
484 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
485 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
486 i->set_active (overlaid != 0 && stacked == 0);
487 i->set_inconsistent (overlaid != 0 && stacked != 0);
488 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
490 layers_items.push_back (
491 RadioMenuElem (layers_group, _("Stacked"),
492 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
495 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
496 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
497 i->set_active (overlaid == 0 && stacked != 0);
498 i->set_inconsistent (overlaid != 0 && stacked != 0);
500 items.push_back (MenuElem (_("Layers"), *layers_menu));
502 if (!Profile->get_sae()) {
504 Menu* alignment_menu = manage (new Menu);
505 MenuList& alignment_items = alignment_menu->items();
506 alignment_menu->set_name ("ArdourContextMenu");
508 RadioMenuItem::Group align_group;
510 /* Same verbose hacks as for the layering options above */
516 boost::shared_ptr<Track> first_track;
518 TrackSelection const & s = _editor.get_selection().tracks;
519 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
520 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
521 if (!r || !r->is_track ()) {
526 first_track = r->track();
529 switch (r->track()->alignment_choice()) {
533 switch (r->track()->alignment_style()) {
534 case ExistingMaterial:
542 case UseExistingMaterial:
558 inconsistent = false;
567 if (!inconsistent && first_track) {
569 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
570 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
571 i->set_active (automatic != 0 && existing == 0 && capture == 0);
572 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
574 switch (first_track->alignment_choice()) {
576 switch (first_track->alignment_style()) {
577 case ExistingMaterial:
578 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
581 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
589 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
590 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
591 i->set_active (existing != 0 && capture == 0 && automatic == 0);
592 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
594 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
595 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
596 i->set_active (existing == 0 && capture != 0 && automatic == 0);
597 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
599 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
605 Menu* mode_menu = manage (new Menu);
606 MenuList& mode_items = mode_menu->items ();
607 mode_menu->set_name ("ArdourContextMenu");
609 RadioMenuItem::Group mode_group;
615 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
616 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
617 if (!r || !r->is_track ()) {
621 switch (r->track()->mode()) {
634 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
635 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
636 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
637 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
638 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
640 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
641 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
642 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
643 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
644 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
646 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
647 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
648 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
649 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
650 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
652 items.push_back (MenuElem (_("Mode"), *mode_menu));
655 color_mode_menu = build_color_mode_menu();
656 if (color_mode_menu) {
657 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
660 items.push_back (SeparatorElem());
662 build_playlist_menu ();
663 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
664 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
666 route_group_menu->detach ();
669 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
670 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
672 r.push_back (rtv->route ());
677 r.push_back (route ());
680 route_group_menu->build (r);
681 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
683 build_automation_action_menu (true);
684 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
686 items.push_back (SeparatorElem());
691 TrackSelection const & s = _editor.get_selection().tracks;
692 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
693 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
698 if (r->route()->active()) {
705 items.push_back (CheckMenuElem (_("Active")));
706 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
707 bool click_sets_active = true;
708 if (active > 0 && inactive == 0) {
709 i->set_active (true);
710 click_sets_active = false;
711 } else if (active > 0 && inactive > 0) {
712 i->set_inconsistent (true);
714 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
716 items.push_back (SeparatorElem());
717 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
718 if (!Profile->get_sae()) {
719 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
721 items.push_front (SeparatorElem());
722 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
727 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
729 if (apply_to_selection) {
730 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
735 if (!track()->can_use_mode (mode, needs_bounce)) {
741 cerr << "would bounce this one\n";
746 track()->set_mode (mode);
748 rec_enable_button->remove ();
751 case ARDOUR::NonLayered:
753 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
755 case ARDOUR::Destructive:
756 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
760 rec_enable_button->show_all ();
765 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
771 TimeAxisView::show_timestretch (start, end);
781 /* check that the time selection was made in our route, or our route group.
782 remember that route_group() == 0 implies the route is *not* in a edit group.
785 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
786 /* this doesn't apply to us */
790 /* ignore it if our edit group is not active */
792 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
797 if (timestretch_rect == 0) {
798 timestretch_rect = new SimpleRect (*canvas_display ());
799 timestretch_rect->property_x1() = 0.0;
800 timestretch_rect->property_y1() = 0.0;
801 timestretch_rect->property_x2() = 0.0;
802 timestretch_rect->property_y2() = 0.0;
803 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
804 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
807 timestretch_rect->show ();
808 timestretch_rect->raise_to_top ();
810 x1 = start / _editor.get_current_zoom();
811 x2 = (end - 1) / _editor.get_current_zoom();
812 y2 = current_height() - 2;
814 timestretch_rect->property_x1() = x1;
815 timestretch_rect->property_y1() = 1.0;
816 timestretch_rect->property_x2() = x2;
817 timestretch_rect->property_y2() = y2;
821 RouteTimeAxisView::hide_timestretch ()
823 TimeAxisView::hide_timestretch ();
825 if (timestretch_rect) {
826 timestretch_rect->hide ();
831 RouteTimeAxisView::show_selection (TimeSelection& ts)
835 /* ignore it if our edit group is not active or if the selection was started
836 in some other track or route group (remember that route_group() == 0 means
837 that the track is not in an route group).
840 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
841 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
847 TimeAxisView::show_selection (ts);
851 RouteTimeAxisView::set_height (uint32_t h)
854 bool height_changed = (height == 0) || (h != height);
855 gm.get_level_meter().setup_meters (gmlen);
857 TimeAxisView::set_height (h);
860 _view->set_height ((double) current_height());
863 if (height >= preset_height (HeightNormal)) {
867 gm.get_gain_slider().show();
869 if (!_route || _route->is_monitor()) {
874 if (rec_enable_button)
875 rec_enable_button->show();
877 route_group_button.show();
878 automation_button.show();
880 if (is_track() && track()->mode() == ARDOUR::Normal) {
881 playlist_button.show();
888 gm.get_gain_slider().hide();
890 if (!_route || _route->is_monitor()) {
895 if (rec_enable_button)
896 rec_enable_button->show();
898 route_group_button.hide ();
899 automation_button.hide ();
901 if (is_track() && track()->mode() == ARDOUR::Normal) {
902 playlist_button.hide ();
907 if (height_changed && !no_redraw) {
908 /* only emit the signal if the height really changed */
914 RouteTimeAxisView::route_color_changed ()
917 _view->apply_color (color(), StreamView::RegionColor);
922 RouteTimeAxisView::reset_samples_per_unit ()
924 set_samples_per_unit (_editor.get_current_zoom());
928 RouteTimeAxisView::horizontal_position_changed ()
931 _view->horizontal_position_changed ();
936 RouteTimeAxisView::set_samples_per_unit (double spu)
941 speed = track()->speed();
945 _view->set_samples_per_unit (spu * speed);
948 TimeAxisView::set_samples_per_unit (spu * speed);
952 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
954 if (!mitem->get_active()) {
955 /* this is one of the two calls made when these radio menu items change status. this one
956 is for the item that became inactive, and we want to ignore it.
961 if (apply_to_selection) {
962 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
965 track()->set_align_choice (choice);
971 RouteTimeAxisView::rename_current_playlist ()
973 ArdourPrompter prompter (true);
976 boost::shared_ptr<Track> tr = track();
977 if (!tr || tr->destructive()) {
981 boost::shared_ptr<Playlist> pl = tr->playlist();
986 prompter.set_title (_("Rename Playlist"));
987 prompter.set_prompt (_("New name for playlist:"));
988 prompter.set_initial_text (pl->name());
989 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
990 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
992 switch (prompter.run ()) {
993 case Gtk::RESPONSE_ACCEPT:
994 prompter.get_result (name);
1006 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1008 std::string ret (basename);
1010 std::string const group_string = "." + route_group()->name() + ".";
1012 // iterate through all playlists
1014 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1015 std::string tmp = (*i)->name();
1017 std::string::size_type idx = tmp.find(group_string);
1018 // find those which belong to this group
1019 if (idx != string::npos) {
1020 tmp = tmp.substr(idx + group_string.length());
1022 // and find the largest current number
1023 int x = atoi(tmp.c_str());
1024 if (x > maxnumber) {
1033 snprintf (buf, sizeof(buf), "%d", maxnumber);
1035 ret = this->name() + "." + route_group()->name () + "." + buf;
1041 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1045 boost::shared_ptr<Track> tr = track ();
1046 if (!tr || tr->destructive()) {
1050 boost::shared_ptr<const Playlist> pl = tr->playlist();
1057 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1058 name = resolve_new_group_playlist_name(name, playlists_before_op);
1061 while (_session->playlists->by_name(name)) {
1062 name = Playlist::bump_name (name, *_session);
1065 // TODO: The prompter "new" button should be de-activated if the user
1066 // specifies a playlist name which already exists in the session.
1070 ArdourPrompter prompter (true);
1072 prompter.set_title (_("New Copy Playlist"));
1073 prompter.set_prompt (_("Name for new playlist:"));
1074 prompter.set_initial_text (name);
1075 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1076 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1077 prompter.show_all ();
1079 switch (prompter.run ()) {
1080 case Gtk::RESPONSE_ACCEPT:
1081 prompter.get_result (name);
1089 if (name.length()) {
1090 tr->use_copy_playlist ();
1091 tr->playlist()->set_name (name);
1096 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1100 boost::shared_ptr<Track> tr = track ();
1101 if (!tr || tr->destructive()) {
1105 boost::shared_ptr<const Playlist> pl = tr->playlist();
1112 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1113 name = resolve_new_group_playlist_name(name,playlists_before_op);
1116 while (_session->playlists->by_name(name)) {
1117 name = Playlist::bump_name (name, *_session);
1123 ArdourPrompter prompter (true);
1125 prompter.set_title (_("New Playlist"));
1126 prompter.set_prompt (_("Name for new playlist:"));
1127 prompter.set_initial_text (name);
1128 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1129 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1131 switch (prompter.run ()) {
1132 case Gtk::RESPONSE_ACCEPT:
1133 prompter.get_result (name);
1141 if (name.length()) {
1142 tr->use_new_playlist ();
1143 tr->playlist()->set_name (name);
1148 RouteTimeAxisView::clear_playlist ()
1150 boost::shared_ptr<Track> tr = track ();
1151 if (!tr || tr->destructive()) {
1155 boost::shared_ptr<Playlist> pl = tr->playlist();
1160 _editor.clear_playlist (pl);
1164 RouteTimeAxisView::speed_changed ()
1166 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1170 RouteTimeAxisView::update_diskstream_display ()
1180 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1182 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1184 /* special case: select/deselect all tracks */
1185 if (_editor.get_selection().selected (this)) {
1186 _editor.get_selection().clear_tracks ();
1188 _editor.select_all_tracks ();
1194 switch (ArdourKeyboard::selection_type (ev->state)) {
1195 case Selection::Toggle:
1196 _editor.get_selection().toggle (this);
1199 case Selection::Set:
1200 _editor.get_selection().set (this);
1203 case Selection::Extend:
1204 _editor.extend_selection_to_track (*this);
1207 case Selection::Add:
1208 _editor.get_selection().add (this);
1214 RouteTimeAxisView::set_selected_points (PointSelection& points)
1216 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1217 (*i)->set_selected_points (points);
1222 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1225 _view->set_selected_regionviews (regions);
1229 /** Add the selectable things that we have to a list.
1230 * @param results List to add things to.
1233 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1238 speed = track()->speed();
1241 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1242 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1244 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1245 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1248 /* pick up visible automation tracks */
1250 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1251 if (!(*i)->hidden()) {
1252 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1258 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1261 _view->get_inverted_selectables (sel, results);
1264 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1265 if (!(*i)->hidden()) {
1266 (*i)->get_inverted_selectables (sel, results);
1274 RouteTimeAxisView::route_group () const
1276 return _route->route_group();
1280 RouteTimeAxisView::name() const
1282 return _route->name();
1285 boost::shared_ptr<Playlist>
1286 RouteTimeAxisView::playlist () const
1288 boost::shared_ptr<Track> tr;
1290 if ((tr = track()) != 0) {
1291 return tr->playlist();
1293 return boost::shared_ptr<Playlist> ();
1298 RouteTimeAxisView::name_entry_changed ()
1300 string x = name_entry.get_text ();
1302 if (x == _route->name()) {
1306 strip_whitespace_edges (x);
1308 if (x.length() == 0) {
1309 name_entry.set_text (_route->name());
1313 if (_session->route_name_internal (x)) {
1314 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1316 name_entry.grab_focus ();
1317 } else if (RouteUI::verify_new_route_name (x)) {
1318 _route->set_name (x);
1320 name_entry.grab_focus ();
1324 boost::shared_ptr<Region>
1325 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1327 boost::shared_ptr<Playlist> pl = playlist ();
1330 return pl->find_next_region (pos, point, dir);
1333 return boost::shared_ptr<Region> ();
1337 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1339 boost::shared_ptr<Playlist> pl = playlist ();
1342 return pl->find_next_region_boundary (pos, dir);
1349 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1351 boost::shared_ptr<Playlist> what_we_got;
1352 boost::shared_ptr<Track> tr = track ();
1353 boost::shared_ptr<Playlist> playlist;
1356 /* route is a bus, not a track */
1360 playlist = tr->playlist();
1362 TimeSelection time (selection.time);
1363 float const speed = tr->speed();
1364 if (speed != 1.0f) {
1365 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1366 (*i).start = session_frame_to_track_frame((*i).start, speed);
1367 (*i).end = session_frame_to_track_frame((*i).end, speed);
1371 playlist->clear_changes ();
1372 playlist->clear_owned_changes ();
1376 if (playlist->cut (time) != 0) {
1377 vector<Command*> cmds;
1378 playlist->rdiff (cmds);
1379 _session->add_commands (cmds);
1381 _session->add_command (new StatefulDiffCommand (playlist));
1386 if ((what_we_got = playlist->cut (time)) != 0) {
1387 _editor.get_cut_buffer().add (what_we_got);
1388 vector<Command*> cmds;
1389 playlist->rdiff (cmds);
1390 _session->add_commands (cmds);
1392 _session->add_command (new StatefulDiffCommand (playlist));
1396 if ((what_we_got = playlist->copy (time)) != 0) {
1397 _editor.get_cut_buffer().add (what_we_got);
1402 if ((what_we_got = playlist->cut (time)) != 0) {
1404 vector<Command*> cmds;
1405 playlist->rdiff (cmds);
1406 _session->add_commands (cmds);
1407 _session->add_command (new StatefulDiffCommand (playlist));
1408 what_we_got->release ();
1415 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1421 boost::shared_ptr<Playlist> pl = playlist ();
1422 PlaylistSelection::iterator p;
1424 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1426 if (p == selection.playlists.end()) {
1430 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1432 if (track()->speed() != 1.0f) {
1433 pos = session_frame_to_track_frame (pos, track()->speed());
1434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1437 pl->clear_changes ();
1438 pl->paste (*p, pos, times);
1439 _session->add_command (new StatefulDiffCommand (pl));
1445 struct PlaylistSorter {
1446 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1447 return a->sort_id() < b->sort_id();
1452 RouteTimeAxisView::build_playlist_menu ()
1454 using namespace Menu_Helpers;
1460 delete playlist_action_menu;
1461 playlist_action_menu = new Menu;
1462 playlist_action_menu->set_name ("ArdourContextMenu");
1464 MenuList& playlist_items = playlist_action_menu->items();
1465 playlist_action_menu->set_name ("ArdourContextMenu");
1466 playlist_items.clear();
1468 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1469 boost::shared_ptr<Track> tr = track();
1470 RadioMenuItem::Group playlist_group;
1472 _session->playlists->get (playlists);
1474 /* find the playlists for this diskstream */
1475 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1476 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1477 playlists_tr.push_back(*i);
1481 /* sort the playlists */
1483 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1485 /* add the playlists to the menu */
1486 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1487 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1488 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1489 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1491 if (tr->playlist()->id() == (*i)->id()) {
1497 playlist_items.push_back (SeparatorElem());
1498 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1499 playlist_items.push_back (SeparatorElem());
1501 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1502 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1503 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1506 // Use a label which tells the user what is happening
1507 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1508 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1512 playlist_items.push_back (SeparatorElem());
1513 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1514 playlist_items.push_back (SeparatorElem());
1516 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1520 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1522 assert (is_track());
1524 // exit if we were triggered by deactivating the old playlist
1525 if (!item->get_active()) {
1529 boost::shared_ptr<Playlist> pl (wpl.lock());
1535 if (track()->playlist() == pl) {
1536 // exit when use_playlist is called by the creation of the playlist menu
1537 // or the playlist choice is unchanged
1541 track()->use_playlist (pl);
1543 RouteGroup* rg = route_group();
1545 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1546 std::string group_string = "." + rg->name() + ".";
1548 std::string take_name = pl->name();
1549 std::string::size_type idx = take_name.find(group_string);
1551 if (idx == std::string::npos)
1554 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1556 boost::shared_ptr<RouteList> rl (rg->route_list());
1558 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1559 if ( (*i) == this->route()) {
1563 std::string playlist_name = (*i)->name()+group_string+take_name;
1565 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1570 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1572 // No playlist for this track for this take yet, make it
1573 track->use_new_playlist();
1574 track->playlist()->set_name(playlist_name);
1576 track->use_playlist(ipl);
1583 RouteTimeAxisView::show_playlist_selector ()
1585 _editor.playlist_selector().show_for (this);
1589 RouteTimeAxisView::map_frozen ()
1595 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1597 switch (track()->freeze_state()) {
1599 playlist_button.set_sensitive (false);
1600 rec_enable_button->set_sensitive (false);
1603 playlist_button.set_sensitive (true);
1604 rec_enable_button->set_sensitive (true);
1610 RouteTimeAxisView::color_handler ()
1612 //case cTimeStretchOutline:
1613 if (timestretch_rect) {
1614 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1616 //case cTimeStretchFill:
1617 if (timestretch_rect) {
1618 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1624 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1625 * Will add track if necessary.
1628 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1630 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1631 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1634 /* it doesn't exist yet, so we don't care about the button state: just add it */
1635 create_automation_child (param, true);
1638 bool yn = menu->get_active();
1639 bool changed = false;
1641 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1643 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1644 will have done that for us.
1647 if (changed && !no_redraw) {
1655 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1657 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1663 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1665 if (menu && !_hidden) {
1666 ignore_toggle = true;
1667 menu->set_active (false);
1668 ignore_toggle = false;
1671 if (_route && !no_redraw) {
1678 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1680 if (apply_to_selection) {
1681 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1685 /* Show our automation */
1687 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1688 i->second->set_marked_for_display (true);
1690 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1693 menu->set_active(true);
1698 /* Show processor automation */
1700 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1701 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1702 if ((*ii)->view == 0) {
1703 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1706 (*ii)->menu_item->set_active (true);
1719 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1721 if (apply_to_selection) {
1722 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1726 /* Show our automation */
1728 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1729 if (i->second->has_automation()) {
1730 i->second->set_marked_for_display (true);
1732 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1734 menu->set_active(true);
1739 /* Show processor automation */
1741 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1742 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1743 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1744 (*ii)->menu_item->set_active (true);
1756 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1758 if (apply_to_selection) {
1759 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1763 /* Hide our automation */
1765 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1766 i->second->set_marked_for_display (false);
1768 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1771 menu->set_active (false);
1775 /* Hide processor automation */
1777 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1778 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1779 (*ii)->menu_item->set_active (false);
1790 RouteTimeAxisView::region_view_added (RegionView* rv)
1792 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1793 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1794 boost::shared_ptr<AutomationTimeAxisView> atv;
1796 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1801 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1802 (*i)->add_ghost(rv);
1806 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1808 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1814 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1816 parent.remove_processor_automation_node (this);
1820 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1823 remove_child (pan->view);
1827 RouteTimeAxisView::ProcessorAutomationNode*
1828 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1830 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1832 if ((*i)->processor == processor) {
1834 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1835 if ((*ii)->what == what) {
1845 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1847 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1850 ProcessorAutomationNode* pan;
1852 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1853 /* session state may never have been saved with new plugin */
1854 error << _("programming error: ")
1855 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1856 processor->name(), what.type(), (int) what.channel(), what.id() )
1866 boost::shared_ptr<AutomationControl> control
1867 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1869 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1870 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1871 _editor, *this, false, parent_canvas,
1872 processor->describe_parameter (what), processor->name()));
1874 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1876 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1879 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1884 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1887 pan->menu_item->set_active (false);
1896 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1898 boost::shared_ptr<Processor> processor (p.lock ());
1900 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1901 /* The Amp processor is a special case and is dealt with separately */
1905 set<Evoral::Parameter> existing;
1907 processor->what_has_data (existing);
1909 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1911 Evoral::Parameter param (*i);
1912 boost::shared_ptr<AutomationLine> al;
1914 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1917 add_processor_automation_curve (processor, param);
1923 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1925 using namespace Menu_Helpers;
1929 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1931 _automation_tracks[param] = track;
1933 /* existing state overrides "show" argument */
1934 string s = track->gui_property ("visible");
1936 show = string_is_affirmative (s);
1939 /* this might or might not change the visibility status, so don't rely on it */
1940 track->set_marked_for_display (show);
1942 if (show && !no_redraw) {
1946 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1947 /* MIDI-related parameters are always in the menu, there's no
1948 reason to rebuild the menu just because we added a automation
1949 lane for one of them. But if we add a non-MIDI automation
1950 lane, then we need to invalidate the display menu.
1952 delete display_menu;
1958 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1960 boost::shared_ptr<Processor> processor (p.lock ());
1962 if (!processor || !processor->display_to_user ()) {
1966 /* we use this override to veto the Amp processor from the plugin menu,
1967 as its automation lane can be accessed using the special "Fader" menu
1971 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1975 using namespace Menu_Helpers;
1976 ProcessorAutomationInfo *rai;
1977 list<ProcessorAutomationInfo*>::iterator x;
1979 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1981 if (automatable.empty()) {
1985 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1986 if ((*x)->processor == processor) {
1991 if (x == processor_automation.end()) {
1993 rai = new ProcessorAutomationInfo (processor);
1994 processor_automation.push_back (rai);
2002 /* any older menu was deleted at the top of processors_changed()
2003 when we cleared the subplugin menu.
2006 rai->menu = manage (new Menu);
2007 MenuList& items = rai->menu->items();
2008 rai->menu->set_name ("ArdourContextMenu");
2012 std::set<Evoral::Parameter> has_visible_automation;
2013 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2015 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2017 ProcessorAutomationNode* pan;
2018 CheckMenuItem* mitem;
2020 string name = processor->describe_parameter (*i);
2022 items.push_back (CheckMenuElem (name));
2023 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2025 _subplugin_menu_map[*i] = mitem;
2027 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2028 mitem->set_active(true);
2031 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2035 pan = new ProcessorAutomationNode (*i, mitem, *this);
2037 rai->lines.push_back (pan);
2041 pan->menu_item = mitem;
2045 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2048 /* add the menu for this processor, because the subplugin
2049 menu is always cleared at the top of processors_changed().
2050 this is the result of some poor design in gtkmm and/or
2054 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2059 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2060 RouteTimeAxisView::ProcessorAutomationNode* pan)
2062 bool showit = pan->menu_item->get_active();
2063 bool redraw = false;
2065 if (pan->view == 0 && showit) {
2066 add_processor_automation_curve (rai->processor, pan->what);
2070 if (pan->view && pan->view->set_marked_for_display (showit)) {
2074 if (redraw && !no_redraw) {
2080 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2082 if (c.type == RouteProcessorChange::MeterPointChange) {
2083 /* nothing to do if only the meter point has changed */
2087 using namespace Menu_Helpers;
2089 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2090 (*i)->valid = false;
2093 setup_processor_menu_and_curves ();
2095 bool deleted_processor_automation = false;
2097 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2099 list<ProcessorAutomationInfo*>::iterator tmp;
2107 processor_automation.erase (i);
2108 deleted_processor_automation = true;
2115 if (deleted_processor_automation && !no_redraw) {
2120 boost::shared_ptr<AutomationLine>
2121 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2123 ProcessorAutomationNode* pan;
2125 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2131 return boost::shared_ptr<AutomationLine>();
2135 RouteTimeAxisView::reset_processor_automation_curves ()
2137 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2143 RouteTimeAxisView::update_rec_display ()
2145 RouteUI::update_rec_display ();
2146 name_entry.set_sensitive (!_route->record_enabled());
2150 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2152 if (apply_to_selection) {
2153 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2157 _view->set_layer_display (d);
2160 set_gui_property (X_("layer-display"), enum_2_string (d));
2165 RouteTimeAxisView::layer_display () const
2168 return _view->layer_display ();
2171 /* we don't know, since we don't have a _view, so just return something */
2177 boost::shared_ptr<AutomationTimeAxisView>
2178 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2180 AutomationTracks::iterator i = _automation_tracks.find(param);
2181 if (i != _automation_tracks.end()) {
2184 return boost::shared_ptr<AutomationTimeAxisView>();
2189 RouteTimeAxisView::fast_update ()
2191 gm.get_level_meter().update_meters ();
2195 RouteTimeAxisView::hide_meter ()
2198 gm.get_level_meter().hide_meters ();
2202 RouteTimeAxisView::show_meter ()
2208 RouteTimeAxisView::reset_meter ()
2210 if (Config->get_show_track_meters()) {
2211 gm.get_level_meter().setup_meters (height-5);
2218 RouteTimeAxisView::clear_meter ()
2220 gm.get_level_meter().clear_meters ();
2224 RouteTimeAxisView::meter_changed ()
2226 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2231 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2237 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2239 using namespace Menu_Helpers;
2241 if (!_underlay_streams.empty()) {
2242 MenuList& parent_items = parent_menu->items();
2243 Menu* gs_menu = manage (new Menu);
2244 gs_menu->set_name ("ArdourContextMenu");
2245 MenuList& gs_items = gs_menu->items();
2247 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2249 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2250 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2251 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2257 RouteTimeAxisView::set_underlay_state()
2259 if (!underlay_xml_node) {
2263 XMLNodeList nlist = underlay_xml_node->children();
2264 XMLNodeConstIterator niter;
2265 XMLNode *child_node;
2267 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2268 child_node = *niter;
2270 if (child_node->name() != "Underlay") {
2274 XMLProperty* prop = child_node->property ("id");
2276 PBD::ID id (prop->value());
2278 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2281 add_underlay(v->view(), false);
2290 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2296 RouteTimeAxisView& other = v->trackview();
2298 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2299 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2300 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2304 _underlay_streams.push_back(v);
2305 other._underlay_mirrors.push_back(this);
2307 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2309 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2311 if (!underlay_xml_node) {
2312 underlay_xml_node = xml_node->add_child("Underlays");
2315 XMLNode* node = underlay_xml_node->add_child("Underlay");
2316 XMLProperty* prop = node->add_property("id");
2317 prop->set_value(v->trackview().route()->id().to_s());
2324 RouteTimeAxisView::remove_underlay (StreamView* v)
2330 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2331 RouteTimeAxisView& other = v->trackview();
2333 if (it != _underlay_streams.end()) {
2334 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2336 if (gm == other._underlay_mirrors.end()) {
2337 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2341 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2343 _underlay_streams.erase(it);
2344 other._underlay_mirrors.erase(gm);
2346 if (underlay_xml_node) {
2347 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2353 RouteTimeAxisView::set_button_names ()
2355 rec_enable_button_label.set_text (_("r"));
2357 if (_route && _route->solo_safe()) {
2358 solo_button_label.set_text (X_("!"));
2360 if (Config->get_solo_control_is_listen_control()) {
2361 switch (Config->get_listen_position()) {
2362 case AfterFaderListen:
2363 solo_button_label.set_text (_("A"));
2365 case PreFaderListen:
2366 solo_button_label.set_text (_("P"));
2370 solo_button_label.set_text (_("s"));
2373 mute_button_label.set_text (_("m"));
2377 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2379 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2380 if (i != _main_automation_menu_map.end()) {
2384 i = _subplugin_menu_map.find (param);
2385 if (i != _subplugin_menu_map.end()) {
2393 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2395 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2397 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2401 gain_track.reset (new AutomationTimeAxisView (_session,
2402 _route, _route->amp(), c, param,
2407 _route->amp()->describe_parameter(param)));
2410 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2413 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2417 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2419 l->push_back (rv->region());
2423 RouteTimeAxisView::combine_regions ()
2425 /* as of may 2011, we do not offer uncombine for MIDI tracks
2428 if (!is_audio_track()) {
2436 Playlist::RegionList selected_regions;
2437 boost::shared_ptr<Playlist> playlist = track()->playlist();
2439 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2441 if (selected_regions.size() < 2) {
2445 playlist->clear_changes ();
2446 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2448 _session->add_command (new StatefulDiffCommand (playlist));
2449 /* make the new region be selected */
2451 return _view->find_view (compound_region);
2455 RouteTimeAxisView::uncombine_regions ()
2457 /* as of may 2011, we do not offer uncombine for MIDI tracks
2459 if (!is_audio_track()) {
2467 Playlist::RegionList selected_regions;
2468 boost::shared_ptr<Playlist> playlist = track()->playlist();
2470 /* have to grab selected regions first because the uncombine is going
2471 * to change that in the middle of the list traverse
2474 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2476 playlist->clear_changes ();
2478 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2479 playlist->uncombine (*i);
2482 _session->add_command (new StatefulDiffCommand (playlist));
2486 RouteTimeAxisView::state_id() const
2488 return string_compose ("rtav %1", _route->id().to_s());