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/route_group.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlist.h"
59 #include "ardour/debug.h"
60 #include "ardour/utils.h"
61 #include "evoral/Parameter.hpp"
63 #include "ardour_ui.h"
65 #include "global_signals.h"
66 #include "route_time_axis.h"
67 #include "automation_time_axis.h"
68 #include "canvas_impl.h"
69 #include "crossfade_view.h"
71 #include "gui_thread.h"
73 #include "playlist_selector.h"
74 #include "point_selection.h"
76 #include "public_editor.h"
77 #include "region_view.h"
78 #include "rgb_macros.h"
79 #include "selection.h"
80 #include "simplerect.h"
81 #include "streamview.h"
83 #include "route_group_menu.h"
85 #include "ardour/track.h"
89 using namespace ARDOUR;
91 using namespace Gtkmm2ext;
93 using namespace Editing;
96 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
99 RouteTimeAxisView::setup_slider_pix ()
101 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
102 throw failed_constructor ();
106 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::shared_ptr<Route> rt, Canvas& canvas)
109 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
110 , parent_canvas (canvas)
111 , button_table (3, 3)
112 , route_group_button (_("g"))
113 , playlist_button (_("p"))
114 , automation_button (_("a"))
115 , gm (sess, slider, true, 115)
117 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
118 gm.get_level_meter().set_no_show_all();
119 gm.get_level_meter().setup_meters(50);
122 playlist_action_menu = 0;
123 automation_action_menu = 0;
124 plugins_submenu_item = 0;
128 if (!_route->is_hidden()) {
129 _marked_for_display = true;
133 update_solo_display ();
135 timestretch_rect = 0;
138 ignore_toggle = false;
140 route_group_button.set_name ("TrackGroupButton");
141 playlist_button.set_name ("TrackPlaylistButton");
142 automation_button.set_name ("TrackAutomationButton");
144 route_group_button.unset_flags (Gtk::CAN_FOCUS);
145 playlist_button.unset_flags (Gtk::CAN_FOCUS);
146 automation_button.unset_flags (Gtk::CAN_FOCUS);
148 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
149 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
150 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
156 rec_enable_button->remove ();
158 switch (track()->mode()) {
160 case ARDOUR::NonLayered:
161 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
163 case ARDOUR::Destructive:
164 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
167 rec_enable_button->show_all ();
169 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
171 if (is_midi_track()) {
172 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
174 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
177 rec_enable_button->set_sensitive (_session->writable());
180 controls_hbox.pack_start(gm.get_level_meter(), false, false);
181 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
182 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
183 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
185 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
187 if (!_route->is_master()) {
188 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
191 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
194 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
195 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
196 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
197 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
198 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
202 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
204 if (is_track() && track()->mode() == ARDOUR::Normal) {
205 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
210 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
211 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
215 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
216 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
218 /* pick up the correct freeze state */
223 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
224 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
225 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
227 PropertyList* plist = new PropertyList();
229 plist->add (ARDOUR::Properties::edit, true);
230 plist->add (ARDOUR::Properties::mute, true);
231 plist->add (ARDOUR::Properties::solo, true);
233 route_group_menu = new RouteGroupMenu (_session, plist);
235 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
236 gm.get_gain_slider().set_name ("TrackGainFader");
239 RouteTimeAxisView::~RouteTimeAxisView ()
241 CatchDeletion (this);
243 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
247 delete playlist_action_menu;
248 playlist_action_menu = 0;
253 _automation_tracks.clear ();
255 delete route_group_menu;
259 RouteTimeAxisView::post_construct ()
261 /* map current state of the route */
263 update_diskstream_display ();
265 _subplugin_menu_map.clear ();
266 subplugin_menu.items().clear ();
267 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
268 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
269 reset_processor_automation_curves ();
273 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
275 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
276 if (_route->route_group()) {
277 _route->route_group()->remove (_route);
283 r.push_back (route ());
285 route_group_menu->build (r);
286 route_group_menu->menu()->popup (ev->button, ev->time);
292 RouteTimeAxisView::playlist_changed ()
298 RouteTimeAxisView::label_view ()
300 string x = _route->name();
302 if (x != name_entry.get_text()) {
303 name_entry.set_text (x);
306 if (x != name_label.get_text()) {
307 name_label.set_text (x);
310 ARDOUR_UI::instance()->set_tip (name_entry, x);
314 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
316 if (what_changed.contains (ARDOUR::Properties::name)) {
322 RouteTimeAxisView::take_name_changed (void *src)
330 RouteTimeAxisView::playlist_click ()
332 build_playlist_menu ();
333 conditionally_add_to_selection ();
334 playlist_action_menu->popup (1, gtk_get_current_event_time());
338 RouteTimeAxisView::automation_click ()
340 conditionally_add_to_selection ();
341 build_automation_action_menu (false);
342 automation_action_menu->popup (1, gtk_get_current_event_time());
346 RouteTimeAxisView::set_state (const XMLNode& node, int version)
348 TimeAxisView::set_state (node, version);
350 XMLNodeList kids = node.children();
351 XMLNodeConstIterator iter;
352 const XMLProperty* prop;
354 if (_view && (prop = node.property ("layer-display"))) {
355 set_layer_display (LayerDisplay (string_2_enum (prop->value(), _view->layer_display ())));
358 for (iter = kids.begin(); iter != kids.end(); ++iter) {
359 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
360 if ((prop = (*iter)->property ("automation-id")) != 0) {
362 Evoral::Parameter param = ARDOUR::EventTypeMap::instance().new_parameter(prop->value());
363 bool show = ((prop = (*iter)->property ("shown")) != 0) && string_is_affirmative (prop->value());
364 create_automation_child(param, show);
366 warning << "Automation child has no ID" << endmsg;
375 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
377 using namespace Menu_Helpers;
379 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
380 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
383 detach_menu (subplugin_menu);
385 _main_automation_menu_map.clear ();
386 delete automation_action_menu;
387 automation_action_menu = new Menu;
389 MenuList& items = automation_action_menu->items();
391 automation_action_menu->set_name ("ArdourContextMenu");
393 items.push_back (MenuElem (_("Show All Automation"),
394 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
396 items.push_back (MenuElem (_("Show Existing Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
399 items.push_back (MenuElem (_("Hide All Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
402 items.push_back (SeparatorElem ());
404 /* Attach the plugin submenu. It may have previously been used elsewhere,
405 so it was detached above */
407 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
408 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
412 RouteTimeAxisView::build_display_menu ()
414 using namespace Menu_Helpers;
418 TimeAxisView::build_display_menu ();
420 /* now fill it with our stuff */
422 MenuList& items = display_menu->items();
423 display_menu->set_name ("ArdourContextMenu");
425 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
428 detach_menu (*_size_menu);
431 items.push_back (MenuElem (_("Height"), *_size_menu));
433 items.push_back (SeparatorElem());
435 if (!Profile->get_sae()) {
436 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
437 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
438 items.push_back (SeparatorElem());
441 // Hook for derived classes to add type specific stuff
442 append_extra_display_menu_items ();
446 Menu* layers_menu = manage (new Menu);
447 MenuList &layers_items = layers_menu->items();
448 layers_menu->set_name("ArdourContextMenu");
450 RadioMenuItem::Group layers_group;
452 /* Find out how many overlaid/stacked tracks we have in the selection */
456 TrackSelection const & s = _editor.get_selection().tracks;
457 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
458 StreamView* v = (*i)->view ();
463 switch (v->layer_display ()) {
473 /* We're not connecting to signal_toggled() here; in the case where these two items are
474 set to be in the `inconsistent' state, it seems that one or other will end up active
475 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
476 select the active one, no toggled signal is emitted so nothing happens.
479 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
480 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
481 i->set_active (overlaid != 0 && stacked == 0);
482 i->set_inconsistent (overlaid != 0 && stacked != 0);
483 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
485 layers_items.push_back (
486 RadioMenuElem (layers_group, _("Stacked"),
487 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
490 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
491 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
492 i->set_active (overlaid == 0 && stacked != 0);
493 i->set_inconsistent (overlaid != 0 && stacked != 0);
495 items.push_back (MenuElem (_("Layers"), *layers_menu));
497 if (!Profile->get_sae()) {
499 Menu* alignment_menu = manage (new Menu);
500 MenuList& alignment_items = alignment_menu->items();
501 alignment_menu->set_name ("ArdourContextMenu");
503 RadioMenuItem::Group align_group;
505 /* Same verbose hacks as for the layering options above */
511 boost::shared_ptr<Track> first_track;
513 TrackSelection const & s = _editor.get_selection().tracks;
514 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
515 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
516 if (!r || !r->is_track ()) {
521 first_track = r->track();
524 switch (r->track()->alignment_choice()) {
528 switch (r->track()->alignment_style()) {
529 case ExistingMaterial:
537 case UseExistingMaterial:
553 inconsistent = false;
562 if (!inconsistent && first_track) {
564 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
565 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
566 i->set_active (automatic != 0 && existing == 0 && capture == 0);
567 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
569 switch (first_track->alignment_choice()) {
571 switch (first_track->alignment_style()) {
572 case ExistingMaterial:
573 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
576 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
584 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
585 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
586 i->set_active (existing != 0 && capture == 0 && automatic == 0);
587 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
589 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
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, UseCaptureTime, true));
594 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
600 Menu* mode_menu = manage (new Menu);
601 MenuList& mode_items = mode_menu->items ();
602 mode_menu->set_name ("ArdourContextMenu");
604 RadioMenuItem::Group mode_group;
610 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
611 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
612 if (!r || !r->is_track ()) {
616 switch (r->track()->mode()) {
629 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
630 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
631 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
632 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
633 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
635 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
636 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
637 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
638 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
639 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
641 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
642 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
643 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
644 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
645 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
647 items.push_back (MenuElem (_("Mode"), *mode_menu));
650 color_mode_menu = build_color_mode_menu();
651 if (color_mode_menu) {
652 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
655 items.push_back (SeparatorElem());
657 build_playlist_menu ();
658 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
659 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
661 route_group_menu->detach ();
664 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
665 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
667 r.push_back (rtv->route ());
672 r.push_back (route ());
675 route_group_menu->build (r);
676 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
678 build_automation_action_menu (true);
679 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
681 items.push_back (SeparatorElem());
686 TrackSelection const & s = _editor.get_selection().tracks;
687 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
688 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
693 if (r->route()->active()) {
700 items.push_back (CheckMenuElem (_("Active")));
701 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
702 bool click_sets_active = true;
703 if (active > 0 && inactive == 0) {
704 i->set_active (true);
705 click_sets_active = false;
706 } else if (active > 0 && inactive > 0) {
707 i->set_inconsistent (true);
709 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
711 items.push_back (SeparatorElem());
712 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
713 if (!Profile->get_sae()) {
714 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
716 items.push_front (SeparatorElem());
717 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
722 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
724 if (apply_to_selection) {
725 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
730 if (!track()->can_use_mode (mode, needs_bounce)) {
736 cerr << "would bounce this one\n";
741 track()->set_mode (mode);
743 rec_enable_button->remove ();
746 case ARDOUR::NonLayered:
748 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
750 case ARDOUR::Destructive:
751 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
755 rec_enable_button->show_all ();
760 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
766 TimeAxisView::show_timestretch (start, end);
776 /* check that the time selection was made in our route, or our route group.
777 remember that route_group() == 0 implies the route is *not* in a edit group.
780 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
781 /* this doesn't apply to us */
785 /* ignore it if our edit group is not active */
787 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
792 if (timestretch_rect == 0) {
793 timestretch_rect = new SimpleRect (*canvas_display ());
794 timestretch_rect->property_x1() = 0.0;
795 timestretch_rect->property_y1() = 0.0;
796 timestretch_rect->property_x2() = 0.0;
797 timestretch_rect->property_y2() = 0.0;
798 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
799 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
802 timestretch_rect->show ();
803 timestretch_rect->raise_to_top ();
805 x1 = start / _editor.get_current_zoom();
806 x2 = (end - 1) / _editor.get_current_zoom();
807 y2 = current_height() - 2;
809 timestretch_rect->property_x1() = x1;
810 timestretch_rect->property_y1() = 1.0;
811 timestretch_rect->property_x2() = x2;
812 timestretch_rect->property_y2() = y2;
816 RouteTimeAxisView::hide_timestretch ()
818 TimeAxisView::hide_timestretch ();
820 if (timestretch_rect) {
821 timestretch_rect->hide ();
826 RouteTimeAxisView::show_selection (TimeSelection& ts)
830 /* ignore it if our edit group is not active or if the selection was started
831 in some other track or route group (remember that route_group() == 0 means
832 that the track is not in an route group).
835 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
836 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
842 TimeAxisView::show_selection (ts);
846 RouteTimeAxisView::set_height (uint32_t h)
849 bool height_changed = (height == 0) || (h != height);
850 gm.get_level_meter().setup_meters (gmlen);
852 TimeAxisView::set_height (h);
857 _view->set_height ((double) current_height());
861 snprintf (buf, sizeof (buf), "%u", height);
862 xml_node->add_property ("height", buf);
864 if (height >= preset_height (HeightNormal)) {
869 gm.get_gain_slider().show();
871 if (!_route || _route->is_monitor()) {
876 if (rec_enable_button)
877 rec_enable_button->show();
879 route_group_button.show();
880 automation_button.show();
882 if (is_track() && track()->mode() == ARDOUR::Normal) {
883 playlist_button.show();
886 } else if (height >= preset_height (HeightSmaller)) {
892 gm.get_gain_slider().hide();
894 if (!_route || _route->is_monitor()) {
899 if (rec_enable_button)
900 rec_enable_button->show();
902 route_group_button.hide ();
903 automation_button.hide ();
905 if (is_track() && track()->mode() == ARDOUR::Normal) {
906 playlist_button.hide ();
912 /* don't allow name_entry to be hidden while
913 it has focus, otherwise the GUI becomes unusable.
916 if (name_entry.has_focus()) {
917 if (name_entry.get_text() != _route->name()) {
918 name_entry_changed ();
920 controls_ebox.grab_focus ();
926 gm.get_gain_slider().hide();
929 if (rec_enable_button)
930 rec_enable_button->hide();
932 route_group_button.hide ();
933 automation_button.hide ();
934 playlist_button.hide ();
935 name_label.set_text (_route->name());
938 if (height_changed && !no_redraw) {
939 /* only emit the signal if the height really changed */
940 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
945 RouteTimeAxisView::set_color (Gdk::Color const & c)
947 RouteUI::set_color (c);
950 _view->apply_color (_color, StreamView::RegionColor);
955 RouteTimeAxisView::reset_samples_per_unit ()
957 set_samples_per_unit (_editor.get_current_zoom());
961 RouteTimeAxisView::horizontal_position_changed ()
964 _view->horizontal_position_changed ();
969 RouteTimeAxisView::set_samples_per_unit (double spu)
974 speed = track()->speed();
978 _view->set_samples_per_unit (spu * speed);
981 TimeAxisView::set_samples_per_unit (spu * speed);
985 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
987 if (!mitem->get_active()) {
988 /* this is one of the two calls made when these radio menu items change status. this one
989 is for the item that became inactive, and we want to ignore it.
994 if (apply_to_selection) {
995 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
998 track()->set_align_choice (choice);
1004 RouteTimeAxisView::rename_current_playlist ()
1006 ArdourPrompter prompter (true);
1009 boost::shared_ptr<Track> tr = track();
1010 if (!tr || tr->destructive()) {
1014 boost::shared_ptr<Playlist> pl = tr->playlist();
1019 prompter.set_title (_("Rename Playlist"));
1020 prompter.set_prompt (_("New name for playlist:"));
1021 prompter.set_initial_text (pl->name());
1022 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1023 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1025 switch (prompter.run ()) {
1026 case Gtk::RESPONSE_ACCEPT:
1027 prompter.get_result (name);
1028 if (name.length()) {
1029 pl->set_name (name);
1039 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1041 std::string ret (basename);
1043 std::string const group_string = "." + route_group()->name() + ".";
1045 // iterate through all playlists
1047 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1048 std::string tmp = (*i)->name();
1050 std::string::size_type idx = tmp.find(group_string);
1051 // find those which belong to this group
1052 if (idx != string::npos) {
1053 tmp = tmp.substr(idx + group_string.length());
1055 // and find the largest current number
1056 int x = atoi(tmp.c_str());
1057 if (x > maxnumber) {
1066 snprintf (buf, sizeof(buf), "%d", maxnumber);
1068 ret = this->name() + "." + route_group()->name () + "." + buf;
1074 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1078 boost::shared_ptr<Track> tr = track ();
1079 if (!tr || tr->destructive()) {
1083 boost::shared_ptr<const Playlist> pl = tr->playlist();
1090 if (route_group() && route_group()->is_active()) {
1091 name = resolve_new_group_playlist_name(name, playlists_before_op);
1094 while (_session->playlists->by_name(name)) {
1095 name = Playlist::bump_name (name, *_session);
1098 // TODO: The prompter "new" button should be de-activated if the user
1099 // specifies a playlist name which already exists in the session.
1103 ArdourPrompter prompter (true);
1105 prompter.set_title (_("New Copy Playlist"));
1106 prompter.set_prompt (_("Name for new playlist:"));
1107 prompter.set_initial_text (name);
1108 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1109 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1110 prompter.show_all ();
1112 switch (prompter.run ()) {
1113 case Gtk::RESPONSE_ACCEPT:
1114 prompter.get_result (name);
1122 if (name.length()) {
1123 tr->use_copy_playlist ();
1124 tr->playlist()->set_name (name);
1129 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1133 boost::shared_ptr<Track> tr = track ();
1134 if (!tr || tr->destructive()) {
1138 boost::shared_ptr<const Playlist> pl = tr->playlist();
1145 if (route_group() && route_group()->is_active()) {
1146 name = resolve_new_group_playlist_name(name,playlists_before_op);
1149 while (_session->playlists->by_name(name)) {
1150 name = Playlist::bump_name (name, *_session);
1156 ArdourPrompter prompter (true);
1158 prompter.set_title (_("New Playlist"));
1159 prompter.set_prompt (_("Name for new playlist:"));
1160 prompter.set_initial_text (name);
1161 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1162 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1164 switch (prompter.run ()) {
1165 case Gtk::RESPONSE_ACCEPT:
1166 prompter.get_result (name);
1174 if (name.length()) {
1175 tr->use_new_playlist ();
1176 tr->playlist()->set_name (name);
1181 RouteTimeAxisView::clear_playlist ()
1183 boost::shared_ptr<Track> tr = track ();
1184 if (!tr || tr->destructive()) {
1188 boost::shared_ptr<Playlist> pl = tr->playlist();
1193 _editor.clear_playlist (pl);
1197 RouteTimeAxisView::speed_changed ()
1199 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1203 RouteTimeAxisView::update_diskstream_display ()
1213 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1215 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1217 /* special case: select/deselect all tracks */
1218 if (_editor.get_selection().selected (this)) {
1219 _editor.get_selection().clear_tracks ();
1221 _editor.select_all_tracks ();
1227 switch (ArdourKeyboard::selection_type (ev->state)) {
1228 case Selection::Toggle:
1229 _editor.get_selection().toggle (this);
1232 case Selection::Set:
1233 _editor.get_selection().set (this);
1236 case Selection::Extend:
1237 _editor.extend_selection_to_track (*this);
1240 case Selection::Add:
1241 _editor.get_selection().add (this);
1247 RouteTimeAxisView::set_selected_points (PointSelection& points)
1249 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1250 (*i)->set_selected_points (points);
1255 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1258 _view->set_selected_regionviews (regions);
1262 /** Add the selectable things that we have to a list.
1263 * @param results List to add things to.
1266 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1271 speed = track()->speed();
1274 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1275 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1277 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1278 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1281 /* pick up visible automation tracks */
1283 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1284 if (!(*i)->hidden()) {
1285 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1291 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1294 _view->get_inverted_selectables (sel, results);
1297 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1298 if (!(*i)->hidden()) {
1299 (*i)->get_inverted_selectables (sel, results);
1307 RouteTimeAxisView::route_group () const
1309 return _route->route_group();
1313 RouteTimeAxisView::name() const
1315 return _route->name();
1318 boost::shared_ptr<Playlist>
1319 RouteTimeAxisView::playlist () const
1321 boost::shared_ptr<Track> tr;
1323 if ((tr = track()) != 0) {
1324 return tr->playlist();
1326 return boost::shared_ptr<Playlist> ();
1331 RouteTimeAxisView::name_entry_changed ()
1335 x = name_entry.get_text ();
1337 if (x == _route->name()) {
1341 strip_whitespace_edges(x);
1343 if (x.length() == 0) {
1344 name_entry.set_text (_route->name());
1348 if (!_session->route_name_unique (x)) {
1349 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1350 name_entry.set_text (_route->name());
1351 } else if (_session->route_name_internal (x)) {
1352 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1354 name_entry.set_text (_route->name());
1356 _route->set_name (x);
1360 boost::shared_ptr<Region>
1361 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1363 boost::shared_ptr<Playlist> pl = playlist ();
1366 return pl->find_next_region (pos, point, dir);
1369 return boost::shared_ptr<Region> ();
1373 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1375 boost::shared_ptr<Playlist> pl = playlist ();
1378 return pl->find_next_region_boundary (pos, dir);
1385 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1387 boost::shared_ptr<Playlist> what_we_got;
1388 boost::shared_ptr<Track> tr = track ();
1389 boost::shared_ptr<Playlist> playlist;
1392 /* route is a bus, not a track */
1396 playlist = tr->playlist();
1398 TimeSelection time (selection.time);
1399 float const speed = tr->speed();
1400 if (speed != 1.0f) {
1401 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1402 (*i).start = session_frame_to_track_frame((*i).start, speed);
1403 (*i).end = session_frame_to_track_frame((*i).end, speed);
1407 playlist->clear_changes ();
1408 playlist->clear_owned_changes ();
1412 if ((what_we_got = playlist->cut (time)) != 0) {
1413 _editor.get_cut_buffer().add (what_we_got);
1415 vector<Command*> cmds;
1416 playlist->rdiff (cmds);
1417 _session->add_commands (cmds);
1419 _session->add_command (new StatefulDiffCommand (playlist));
1423 if ((what_we_got = playlist->copy (time)) != 0) {
1424 _editor.get_cut_buffer().add (what_we_got);
1429 if ((what_we_got = playlist->cut (time)) != 0) {
1431 vector<Command*> cmds;
1432 playlist->rdiff (cmds);
1433 _session->add_commands (cmds);
1434 _session->add_command (new StatefulDiffCommand (playlist));
1435 what_we_got->release ();
1442 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1448 boost::shared_ptr<Playlist> pl = playlist ();
1449 PlaylistSelection::iterator p;
1451 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1453 if (p == selection.playlists.end()) {
1457 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1459 if (track()->speed() != 1.0f) {
1460 pos = session_frame_to_track_frame (pos, track()->speed());
1461 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1464 pl->clear_changes ();
1465 pl->paste (*p, pos, times);
1466 _session->add_command (new StatefulDiffCommand (pl));
1472 struct PlaylistSorter {
1473 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1474 return a->sort_id() < b->sort_id();
1479 RouteTimeAxisView::build_playlist_menu ()
1481 using namespace Menu_Helpers;
1487 delete playlist_action_menu;
1488 playlist_action_menu = new Menu;
1489 playlist_action_menu->set_name ("ArdourContextMenu");
1491 MenuList& playlist_items = playlist_action_menu->items();
1492 playlist_action_menu->set_name ("ArdourContextMenu");
1493 playlist_items.clear();
1495 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1496 boost::shared_ptr<Track> tr = track();
1497 RadioMenuItem::Group playlist_group;
1499 _session->playlists->get (playlists);
1501 /* find the playlists for this diskstream */
1502 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1503 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1504 playlists_tr.push_back(*i);
1508 /* sort the playlists */
1510 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1512 /* add the playlists to the menu */
1513 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1514 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1515 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1516 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1518 if (tr->playlist()->id() == (*i)->id()) {
1524 playlist_items.push_back (SeparatorElem());
1525 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1526 playlist_items.push_back (SeparatorElem());
1528 if (!route_group() || !route_group()->is_active()) {
1529 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1530 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1533 // Use a label which tells the user what is happening
1534 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1535 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1539 playlist_items.push_back (SeparatorElem());
1540 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1541 playlist_items.push_back (SeparatorElem());
1543 playlist_items.push_back (MenuElem(_("Select from all..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1547 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1549 assert (is_track());
1551 // exit if we were triggered by deactivating the old playlist
1552 if (!item->get_active()) {
1556 boost::shared_ptr<Playlist> pl (wpl.lock());
1562 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1565 if (track()->playlist() == apl) {
1566 // exit when use_playlist is called by the creation of the playlist menu
1567 // or the playlist choice is unchanged
1570 track()->use_playlist (apl);
1572 if (route_group() && route_group()->is_active()) {
1573 std::string group_string = "."+route_group()->name()+".";
1575 std::string take_name = apl->name();
1576 std::string::size_type idx = take_name.find(group_string);
1578 if (idx == std::string::npos)
1581 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1583 boost::shared_ptr<RouteList> rl (route_group()->route_list());
1585 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1586 if ( (*i) == this->route()) {
1590 std::string playlist_name = (*i)->name()+group_string+take_name;
1592 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1597 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1599 // No playlist for this track for this take yet, make it
1600 track->use_new_playlist();
1601 track->playlist()->set_name(playlist_name);
1603 track->use_playlist(ipl);
1611 RouteTimeAxisView::show_playlist_selector ()
1613 _editor.playlist_selector().show_for (this);
1617 RouteTimeAxisView::map_frozen ()
1623 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1625 switch (track()->freeze_state()) {
1627 playlist_button.set_sensitive (false);
1628 rec_enable_button->set_sensitive (false);
1631 playlist_button.set_sensitive (true);
1632 rec_enable_button->set_sensitive (true);
1638 RouteTimeAxisView::color_handler ()
1640 //case cTimeStretchOutline:
1641 if (timestretch_rect) {
1642 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1644 //case cTimeStretchFill:
1645 if (timestretch_rect) {
1646 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1652 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1653 * Will add track if necessary.
1656 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1658 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1659 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1662 /* it doesn't exist yet, so we don't care about the button state: just add it */
1663 create_automation_child (param, true);
1666 bool yn = menu->get_active();
1667 if (track->set_visibility (menu->get_active()) && yn) {
1669 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1670 will have done that for us.
1674 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1681 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1683 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1689 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1691 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1692 track->get_state_node()->add_property (X_("shown"), X_("no"));
1694 if (menu && !_hidden) {
1695 ignore_toggle = true;
1696 menu->set_active (false);
1697 ignore_toggle = false;
1700 if (_route && !no_redraw) {
1701 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1707 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1709 if (apply_to_selection) {
1710 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1714 /* Show our automation */
1716 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1717 i->second->set_marked_for_display (true);
1718 i->second->canvas_display()->show();
1719 i->second->get_state_node()->add_property ("shown", X_("yes"));
1721 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1724 menu->set_active(true);
1729 /* Show processor automation */
1731 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1732 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1733 if ((*ii)->view == 0) {
1734 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1737 (*ii)->menu_item->set_active (true);
1745 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1750 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1752 if (apply_to_selection) {
1753 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1757 /* Show our automation */
1759 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1760 if (i->second->has_automation()) {
1761 i->second->set_marked_for_display (true);
1762 i->second->canvas_display()->show();
1763 i->second->get_state_node()->add_property ("shown", X_("yes"));
1765 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1767 menu->set_active(true);
1773 /* Show 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 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1778 (*ii)->menu_item->set_active (true);
1785 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1790 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1792 if (apply_to_selection) {
1793 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1797 /* Hide our automation */
1799 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1800 i->second->set_marked_for_display (false);
1802 i->second->get_state_node()->add_property ("shown", X_("no"));
1804 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1807 menu->set_active (false);
1811 /* Hide processor automation */
1813 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1814 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1815 (*ii)->menu_item->set_active (false);
1820 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1826 RouteTimeAxisView::region_view_added (RegionView* rv)
1828 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1829 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1830 boost::shared_ptr<AutomationTimeAxisView> atv;
1832 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1837 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1838 (*i)->add_ghost(rv);
1842 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1844 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1850 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1852 parent.remove_processor_automation_node (this);
1856 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1859 remove_child (pan->view);
1863 RouteTimeAxisView::ProcessorAutomationNode*
1864 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1866 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1868 if ((*i)->processor == processor) {
1870 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1871 if ((*ii)->what == what) {
1882 legalize_for_xml_node (string str)
1884 string::size_type pos;
1885 string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1891 while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1892 legal.replace (pos, 1, "_");
1901 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1904 ProcessorAutomationNode* pan;
1906 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1907 /* session state may never have been saved with new plugin */
1908 error << _("programming error: ")
1909 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1910 processor->name(), what.type(), (int) what.channel(), what.id() )
1920 name = processor->describe_parameter (what);
1922 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1926 char state_name[256];
1927 snprintf (state_name, sizeof (state_name), "%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1929 boost::shared_ptr<AutomationControl> control
1930 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1932 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1933 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1934 _editor, *this, false, parent_canvas, name, state_name));
1936 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1938 if (!pan->view->marked_for_display()) {
1941 pan->menu_item->set_active (true);
1944 add_child (pan->view);
1947 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1950 processor->mark_automation_visible (what, true);
1954 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1957 pan->menu_item->set_active (false);
1960 i->mark_automation_visible (pan->what, false);
1963 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1968 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1970 boost::shared_ptr<Processor> processor (p.lock ());
1975 set<Evoral::Parameter> s;
1976 boost::shared_ptr<AutomationLine> al;
1978 processor->what_has_visible_data (s);
1980 for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1982 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1985 add_processor_automation_curve (processor, (*i));
1991 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1993 using namespace Menu_Helpers;
2000 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2002 bool hideit = (!show);
2004 if ((node = track->get_state_node()) != 0) {
2005 if ((prop = node->property ("shown")) != 0) {
2006 if (string_is_affirmative (prop->value())) {
2012 _automation_tracks[param] = track;
2014 track->set_visibility (!hideit);
2017 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2020 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2021 /* MIDI-related parameters are always in the menu, there's no
2022 reason to rebuild the menu just because we added a automation
2023 lane for one of them. But if we add a non-MIDI automation
2024 lane, then we need to invalidate the display menu.
2026 delete display_menu;
2032 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2034 boost::shared_ptr<Processor> processor (p.lock ());
2036 if (!processor || !processor->display_to_user ()) {
2040 using namespace Menu_Helpers;
2041 ProcessorAutomationInfo *rai;
2042 list<ProcessorAutomationInfo*>::iterator x;
2044 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2045 std::set<Evoral::Parameter> has_visible_automation;
2047 processor->what_has_visible_data(has_visible_automation);
2049 if (automatable.empty()) {
2053 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2054 if ((*x)->processor == processor) {
2059 if (x == processor_automation.end()) {
2061 rai = new ProcessorAutomationInfo (processor);
2062 processor_automation.push_back (rai);
2070 /* any older menu was deleted at the top of processors_changed()
2071 when we cleared the subplugin menu.
2074 rai->menu = manage (new Menu);
2075 MenuList& items = rai->menu->items();
2076 rai->menu->set_name ("ArdourContextMenu");
2080 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2082 ProcessorAutomationNode* pan;
2083 CheckMenuItem* mitem;
2085 string name = processor->describe_parameter (*i);
2087 items.push_back (CheckMenuElem (name));
2088 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2090 _subplugin_menu_map[*i] = mitem;
2092 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2093 mitem->set_active(true);
2096 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2100 pan = new ProcessorAutomationNode (*i, mitem, *this);
2102 rai->lines.push_back (pan);
2106 pan->menu_item = mitem;
2110 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2113 /* add the menu for this processor, because the subplugin
2114 menu is always cleared at the top of processors_changed().
2115 this is the result of some poor design in gtkmm and/or
2119 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2124 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2125 RouteTimeAxisView::ProcessorAutomationNode* pan)
2127 bool showit = pan->menu_item->get_active();
2128 bool redraw = false;
2130 if (pan->view == 0 && showit) {
2131 add_processor_automation_curve (rai->processor, pan->what);
2135 if (pan->view && showit != pan->view->marked_for_display()) {
2138 pan->view->set_marked_for_display (true);
2139 pan->view->canvas_display()->show();
2140 pan->view->canvas_background()->show();
2142 rai->processor->mark_automation_visible (pan->what, true);
2143 pan->view->set_marked_for_display (false);
2151 if (redraw && !no_redraw) {
2152 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2158 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2160 if (c.type == RouteProcessorChange::MeterPointChange) {
2161 /* nothing to do if only the meter point has changed */
2165 using namespace Menu_Helpers;
2167 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2168 (*i)->valid = false;
2171 _subplugin_menu_map.clear ();
2172 subplugin_menu.items().clear ();
2174 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2175 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2177 bool deleted_processor_automation = false;
2179 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2181 list<ProcessorAutomationInfo*>::iterator tmp;
2189 processor_automation.erase (i);
2190 deleted_processor_automation = true;
2197 if (deleted_processor_automation && !no_redraw) {
2198 _route->gui_changed ("track_height", this);
2202 boost::shared_ptr<AutomationLine>
2203 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2205 ProcessorAutomationNode* pan;
2207 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2213 return boost::shared_ptr<AutomationLine>();
2217 RouteTimeAxisView::reset_processor_automation_curves ()
2219 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2225 RouteTimeAxisView::update_rec_display ()
2227 RouteUI::update_rec_display ();
2228 name_entry.set_sensitive (!_route->record_enabled());
2232 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2234 if (apply_to_selection) {
2235 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2239 _view->set_layer_display (d);
2243 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2248 RouteTimeAxisView::layer_display () const
2251 return _view->layer_display ();
2254 /* we don't know, since we don't have a _view, so just return something */
2260 boost::shared_ptr<AutomationTimeAxisView>
2261 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2263 AutomationTracks::iterator i = _automation_tracks.find(param);
2264 if (i != _automation_tracks.end()) {
2267 return boost::shared_ptr<AutomationTimeAxisView>();
2272 RouteTimeAxisView::fast_update ()
2274 gm.get_level_meter().update_meters ();
2278 RouteTimeAxisView::hide_meter ()
2281 gm.get_level_meter().hide_meters ();
2285 RouteTimeAxisView::show_meter ()
2291 RouteTimeAxisView::reset_meter ()
2293 if (Config->get_show_track_meters()) {
2294 gm.get_level_meter().setup_meters (height-5);
2301 RouteTimeAxisView::clear_meter ()
2303 gm.get_level_meter().clear_meters ();
2307 RouteTimeAxisView::meter_changed ()
2309 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2314 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2320 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2322 using namespace Menu_Helpers;
2324 if (!_underlay_streams.empty()) {
2325 MenuList& parent_items = parent_menu->items();
2326 Menu* gs_menu = manage (new Menu);
2327 gs_menu->set_name ("ArdourContextMenu");
2328 MenuList& gs_items = gs_menu->items();
2330 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2332 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2333 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2334 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2340 RouteTimeAxisView::set_underlay_state()
2342 if (!underlay_xml_node) {
2346 XMLNodeList nlist = underlay_xml_node->children();
2347 XMLNodeConstIterator niter;
2348 XMLNode *child_node;
2350 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2351 child_node = *niter;
2353 if (child_node->name() != "Underlay") {
2357 XMLProperty* prop = child_node->property ("id");
2359 PBD::ID id (prop->value());
2361 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2364 add_underlay(v->view(), false);
2373 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2379 RouteTimeAxisView& other = v->trackview();
2381 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2382 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2383 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2387 _underlay_streams.push_back(v);
2388 other._underlay_mirrors.push_back(this);
2390 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2393 if (!underlay_xml_node) {
2395 underlay_xml_node = xml_node->add_child("Underlays");
2398 XMLNode* node = underlay_xml_node->add_child("Underlay");
2399 XMLProperty* prop = node->add_property("id");
2400 prop->set_value(v->trackview().route()->id().to_s());
2406 RouteTimeAxisView::remove_underlay (StreamView* v)
2412 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2413 RouteTimeAxisView& other = v->trackview();
2415 if (it != _underlay_streams.end()) {
2416 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2418 if (gm == other._underlay_mirrors.end()) {
2419 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2423 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2425 _underlay_streams.erase(it);
2426 other._underlay_mirrors.erase(gm);
2428 if (underlay_xml_node) {
2429 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2435 RouteTimeAxisView::set_button_names ()
2437 rec_enable_button_label.set_text (_("r"));
2439 if (_route && _route->solo_safe()) {
2440 solo_button_label.set_text (X_("!"));
2442 if (Config->get_solo_control_is_listen_control()) {
2443 switch (Config->get_listen_position()) {
2444 case AfterFaderListen:
2445 solo_button_label.set_text (_("A"));
2447 case PreFaderListen:
2448 solo_button_label.set_text (_("P"));
2452 solo_button_label.set_text (_("s"));
2455 mute_button_label.set_text (_("m"));
2459 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2461 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2462 if (i != _main_automation_menu_map.end()) {
2466 i = _subplugin_menu_map.find (param);
2467 if (i != _subplugin_menu_map.end()) {
2475 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2477 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2479 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2483 gain_track.reset (new AutomationTimeAxisView (_session,
2484 _route, _route->amp(), c, param,
2489 _route->amp()->describe_parameter(param)));
2492 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2495 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);