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 /* this is one of the two calls made when these radio menu items change status. this one
988 is for the item that became inactive, and we want to ignore it.
991 if (!mitem->get_active()) {
995 if (apply_to_selection) {
996 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
999 track()->set_align_choice (choice);
1005 RouteTimeAxisView::rename_current_playlist ()
1007 ArdourPrompter prompter (true);
1010 boost::shared_ptr<Track> tr = track();
1011 if (!tr || tr->destructive()) {
1015 boost::shared_ptr<Playlist> pl = tr->playlist();
1020 prompter.set_title (_("Rename Playlist"));
1021 prompter.set_prompt (_("New name for playlist:"));
1022 prompter.set_initial_text (pl->name());
1023 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1024 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1026 switch (prompter.run ()) {
1027 case Gtk::RESPONSE_ACCEPT:
1028 prompter.get_result (name);
1029 if (name.length()) {
1030 pl->set_name (name);
1040 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1042 std::string ret (basename);
1044 std::string const group_string = "." + route_group()->name() + ".";
1046 // iterate through all playlists
1048 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1049 std::string tmp = (*i)->name();
1051 std::string::size_type idx = tmp.find(group_string);
1052 // find those which belong to this group
1053 if (idx != string::npos) {
1054 tmp = tmp.substr(idx + group_string.length());
1056 // and find the largest current number
1057 int x = atoi(tmp.c_str());
1058 if (x > maxnumber) {
1067 snprintf (buf, sizeof(buf), "%d", maxnumber);
1069 ret = this->name() + "." + route_group()->name () + "." + buf;
1075 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1079 boost::shared_ptr<Track> tr = track ();
1080 if (!tr || tr->destructive()) {
1084 boost::shared_ptr<const Playlist> pl = tr->playlist();
1091 if (route_group() && route_group()->is_active()) {
1092 name = resolve_new_group_playlist_name(name, playlists_before_op);
1095 while (_session->playlists->by_name(name)) {
1096 name = Playlist::bump_name (name, *_session);
1099 // TODO: The prompter "new" button should be de-activated if the user
1100 // specifies a playlist name which already exists in the session.
1104 ArdourPrompter prompter (true);
1106 prompter.set_title (_("New Copy Playlist"));
1107 prompter.set_prompt (_("Name for new playlist:"));
1108 prompter.set_initial_text (name);
1109 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1110 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1111 prompter.show_all ();
1113 switch (prompter.run ()) {
1114 case Gtk::RESPONSE_ACCEPT:
1115 prompter.get_result (name);
1123 if (name.length()) {
1124 tr->use_copy_playlist ();
1125 tr->playlist()->set_name (name);
1130 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1134 boost::shared_ptr<Track> tr = track ();
1135 if (!tr || tr->destructive()) {
1139 boost::shared_ptr<const Playlist> pl = tr->playlist();
1146 if (route_group() && route_group()->is_active()) {
1147 name = resolve_new_group_playlist_name(name,playlists_before_op);
1150 while (_session->playlists->by_name(name)) {
1151 name = Playlist::bump_name (name, *_session);
1157 ArdourPrompter prompter (true);
1159 prompter.set_title (_("New Playlist"));
1160 prompter.set_prompt (_("Name for new playlist:"));
1161 prompter.set_initial_text (name);
1162 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1163 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1165 switch (prompter.run ()) {
1166 case Gtk::RESPONSE_ACCEPT:
1167 prompter.get_result (name);
1175 if (name.length()) {
1176 tr->use_new_playlist ();
1177 tr->playlist()->set_name (name);
1182 RouteTimeAxisView::clear_playlist ()
1184 boost::shared_ptr<Track> tr = track ();
1185 if (!tr || tr->destructive()) {
1189 boost::shared_ptr<Playlist> pl = tr->playlist();
1194 _editor.clear_playlist (pl);
1198 RouteTimeAxisView::speed_changed ()
1200 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1204 RouteTimeAxisView::update_diskstream_display ()
1214 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1216 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1218 /* special case: select/deselect all tracks */
1219 if (_editor.get_selection().selected (this)) {
1220 _editor.get_selection().clear_tracks ();
1222 _editor.select_all_tracks ();
1228 switch (ArdourKeyboard::selection_type (ev->state)) {
1229 case Selection::Toggle:
1230 _editor.get_selection().toggle (this);
1233 case Selection::Set:
1234 _editor.get_selection().set (this);
1237 case Selection::Extend:
1238 _editor.extend_selection_to_track (*this);
1241 case Selection::Add:
1242 _editor.get_selection().add (this);
1248 RouteTimeAxisView::set_selected_points (PointSelection& points)
1250 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1251 (*i)->set_selected_points (points);
1256 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1259 _view->set_selected_regionviews (regions);
1263 /** Add the selectable things that we have to a list.
1264 * @param results List to add things to.
1267 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1272 speed = track()->speed();
1275 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1276 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1278 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1279 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1282 /* pick up visible automation tracks */
1284 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1285 if (!(*i)->hidden()) {
1286 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1292 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1295 _view->get_inverted_selectables (sel, results);
1298 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1299 if (!(*i)->hidden()) {
1300 (*i)->get_inverted_selectables (sel, results);
1308 RouteTimeAxisView::route_group () const
1310 return _route->route_group();
1314 RouteTimeAxisView::name() const
1316 return _route->name();
1319 boost::shared_ptr<Playlist>
1320 RouteTimeAxisView::playlist () const
1322 boost::shared_ptr<Track> tr;
1324 if ((tr = track()) != 0) {
1325 return tr->playlist();
1327 return boost::shared_ptr<Playlist> ();
1332 RouteTimeAxisView::name_entry_changed ()
1336 x = name_entry.get_text ();
1338 if (x == _route->name()) {
1342 strip_whitespace_edges(x);
1344 if (x.length() == 0) {
1345 name_entry.set_text (_route->name());
1349 if (!_session->route_name_unique (x)) {
1350 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1351 name_entry.set_text (_route->name());
1352 } else if (_session->route_name_internal (x)) {
1353 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1355 name_entry.set_text (_route->name());
1357 _route->set_name (x);
1361 boost::shared_ptr<Region>
1362 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1364 boost::shared_ptr<Playlist> pl = playlist ();
1367 return pl->find_next_region (pos, point, dir);
1370 return boost::shared_ptr<Region> ();
1374 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1376 boost::shared_ptr<Playlist> pl = playlist ();
1379 return pl->find_next_region_boundary (pos, dir);
1386 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1388 boost::shared_ptr<Playlist> what_we_got;
1389 boost::shared_ptr<Track> tr = track ();
1390 boost::shared_ptr<Playlist> playlist;
1393 /* route is a bus, not a track */
1397 playlist = tr->playlist();
1399 TimeSelection time (selection.time);
1400 float const speed = tr->speed();
1401 if (speed != 1.0f) {
1402 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1403 (*i).start = session_frame_to_track_frame((*i).start, speed);
1404 (*i).end = session_frame_to_track_frame((*i).end, speed);
1408 playlist->clear_changes ();
1409 playlist->clear_owned_changes ();
1413 if ((what_we_got = playlist->cut (time)) != 0) {
1414 _editor.get_cut_buffer().add (what_we_got);
1416 vector<Command*> cmds;
1417 playlist->rdiff (cmds);
1418 _session->add_commands (cmds);
1420 _session->add_command (new StatefulDiffCommand (playlist));
1424 if ((what_we_got = playlist->copy (time)) != 0) {
1425 _editor.get_cut_buffer().add (what_we_got);
1430 if ((what_we_got = playlist->cut (time)) != 0) {
1432 vector<Command*> cmds;
1433 playlist->rdiff (cmds);
1434 _session->add_commands (cmds);
1435 _session->add_command (new StatefulDiffCommand (playlist));
1436 what_we_got->release ();
1443 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1449 boost::shared_ptr<Playlist> pl = playlist ();
1450 PlaylistSelection::iterator p;
1452 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1454 if (p == selection.playlists.end()) {
1458 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1460 if (track()->speed() != 1.0f) {
1461 pos = session_frame_to_track_frame (pos, track()->speed());
1462 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1465 pl->clear_changes ();
1466 pl->paste (*p, pos, times);
1467 _session->add_command (new StatefulDiffCommand (pl));
1473 struct PlaylistSorter {
1474 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1475 return a->sort_id() < b->sort_id();
1480 RouteTimeAxisView::build_playlist_menu ()
1482 using namespace Menu_Helpers;
1488 delete playlist_action_menu;
1489 playlist_action_menu = new Menu;
1490 playlist_action_menu->set_name ("ArdourContextMenu");
1492 MenuList& playlist_items = playlist_action_menu->items();
1493 playlist_action_menu->set_name ("ArdourContextMenu");
1494 playlist_items.clear();
1496 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1497 boost::shared_ptr<Track> tr = track();
1498 RadioMenuItem::Group playlist_group;
1500 _session->playlists->get (playlists);
1502 /* find the playlists for this diskstream */
1503 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1504 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1505 playlists_tr.push_back(*i);
1509 /* sort the playlists */
1511 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1513 /* add the playlists to the menu */
1514 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1515 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1516 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1517 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1519 if (tr->playlist()->id() == (*i)->id()) {
1525 playlist_items.push_back (SeparatorElem());
1526 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1527 playlist_items.push_back (SeparatorElem());
1529 if (!route_group() || !route_group()->is_active()) {
1530 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1531 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1534 // Use a label which tells the user what is happening
1535 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1536 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1540 playlist_items.push_back (SeparatorElem());
1541 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1542 playlist_items.push_back (SeparatorElem());
1544 playlist_items.push_back (MenuElem(_("Select from all..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1548 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1550 assert (is_track());
1552 // exit if we were triggered by deactivating the old playlist
1553 if (!item->get_active()) {
1557 boost::shared_ptr<Playlist> pl (wpl.lock());
1563 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1566 if (track()->playlist() == apl) {
1567 // exit when use_playlist is called by the creation of the playlist menu
1568 // or the playlist choice is unchanged
1571 track()->use_playlist (apl);
1573 if (route_group() && route_group()->is_active()) {
1574 std::string group_string = "."+route_group()->name()+".";
1576 std::string take_name = apl->name();
1577 std::string::size_type idx = take_name.find(group_string);
1579 if (idx == std::string::npos)
1582 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1584 boost::shared_ptr<RouteList> rl (route_group()->route_list());
1586 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1587 if ( (*i) == this->route()) {
1591 std::string playlist_name = (*i)->name()+group_string+take_name;
1593 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1598 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1600 // No playlist for this track for this take yet, make it
1601 track->use_new_playlist();
1602 track->playlist()->set_name(playlist_name);
1604 track->use_playlist(ipl);
1612 RouteTimeAxisView::show_playlist_selector ()
1614 _editor.playlist_selector().show_for (this);
1618 RouteTimeAxisView::map_frozen ()
1624 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1626 switch (track()->freeze_state()) {
1628 playlist_button.set_sensitive (false);
1629 rec_enable_button->set_sensitive (false);
1632 playlist_button.set_sensitive (true);
1633 rec_enable_button->set_sensitive (true);
1639 RouteTimeAxisView::color_handler ()
1641 //case cTimeStretchOutline:
1642 if (timestretch_rect) {
1643 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1645 //case cTimeStretchFill:
1646 if (timestretch_rect) {
1647 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1653 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1654 * Will add track if necessary.
1657 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1659 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1660 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1663 /* it doesn't exist yet, so we don't care about the button state: just add it */
1664 create_automation_child (param, true);
1667 bool yn = menu->get_active();
1668 if (track->set_visibility (menu->get_active()) && yn) {
1670 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1671 will have done that for us.
1675 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1682 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1684 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1690 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1692 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1693 track->get_state_node()->add_property (X_("shown"), X_("no"));
1695 if (menu && !_hidden) {
1696 ignore_toggle = true;
1697 menu->set_active (false);
1698 ignore_toggle = false;
1701 if (_route && !no_redraw) {
1702 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1708 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1710 if (apply_to_selection) {
1711 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1715 /* Show our automation */
1717 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1718 i->second->set_marked_for_display (true);
1719 i->second->canvas_display()->show();
1720 i->second->get_state_node()->add_property ("shown", X_("yes"));
1722 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1725 menu->set_active(true);
1730 /* Show processor automation */
1732 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1733 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1734 if ((*ii)->view == 0) {
1735 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1738 (*ii)->menu_item->set_active (true);
1746 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1751 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1753 if (apply_to_selection) {
1754 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1758 /* Show our automation */
1760 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1761 if (i->second->has_automation()) {
1762 i->second->set_marked_for_display (true);
1763 i->second->canvas_display()->show();
1764 i->second->get_state_node()->add_property ("shown", X_("yes"));
1766 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1768 menu->set_active(true);
1774 /* Show processor automation */
1776 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1777 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1778 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1779 (*ii)->menu_item->set_active (true);
1786 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1791 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1793 if (apply_to_selection) {
1794 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1798 /* Hide our automation */
1800 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1801 i->second->set_marked_for_display (false);
1803 i->second->get_state_node()->add_property ("shown", X_("no"));
1805 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1808 menu->set_active (false);
1812 /* Hide processor automation */
1814 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1815 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1816 (*ii)->menu_item->set_active (false);
1821 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1827 RouteTimeAxisView::region_view_added (RegionView* rv)
1829 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1830 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1831 boost::shared_ptr<AutomationTimeAxisView> atv;
1833 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1838 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1839 (*i)->add_ghost(rv);
1843 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1845 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1851 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1853 parent.remove_processor_automation_node (this);
1857 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1860 remove_child (pan->view);
1864 RouteTimeAxisView::ProcessorAutomationNode*
1865 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1867 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1869 if ((*i)->processor == processor) {
1871 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1872 if ((*ii)->what == what) {
1883 legalize_for_xml_node (string str)
1885 string::size_type pos;
1886 string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1892 while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1893 legal.replace (pos, 1, "_");
1902 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1905 ProcessorAutomationNode* pan;
1907 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1908 /* session state may never have been saved with new plugin */
1909 error << _("programming error: ")
1910 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1911 processor->name(), what.type(), (int) what.channel(), what.id() )
1921 name = processor->describe_parameter (what);
1923 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1927 char state_name[256];
1928 snprintf (state_name, sizeof (state_name), "%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1930 boost::shared_ptr<AutomationControl> control
1931 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1933 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1934 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1935 _editor, *this, false, parent_canvas, name, state_name));
1937 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1939 if (!pan->view->marked_for_display()) {
1942 pan->menu_item->set_active (true);
1945 add_child (pan->view);
1948 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1951 processor->mark_automation_visible (what, true);
1955 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1958 pan->menu_item->set_active (false);
1961 i->mark_automation_visible (pan->what, false);
1964 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1969 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1971 boost::shared_ptr<Processor> processor (p.lock ());
1976 set<Evoral::Parameter> s;
1977 boost::shared_ptr<AutomationLine> al;
1979 processor->what_has_visible_data (s);
1981 for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1983 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1986 add_processor_automation_curve (processor, (*i));
1992 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1994 using namespace Menu_Helpers;
2001 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
2003 bool hideit = (!show);
2005 if ((node = track->get_state_node()) != 0) {
2006 if ((prop = node->property ("shown")) != 0) {
2007 if (string_is_affirmative (prop->value())) {
2013 _automation_tracks[param] = track;
2015 track->set_visibility (!hideit);
2018 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2021 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2022 /* MIDI-related parameters are always in the menu, there's no
2023 reason to rebuild the menu just because we added a automation
2024 lane for one of them. But if we add a non-MIDI automation
2025 lane, then we need to invalidate the display menu.
2027 delete display_menu;
2033 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2035 boost::shared_ptr<Processor> processor (p.lock ());
2037 if (!processor || !processor->display_to_user ()) {
2041 using namespace Menu_Helpers;
2042 ProcessorAutomationInfo *rai;
2043 list<ProcessorAutomationInfo*>::iterator x;
2045 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2046 std::set<Evoral::Parameter> has_visible_automation;
2048 processor->what_has_visible_data(has_visible_automation);
2050 if (automatable.empty()) {
2054 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2055 if ((*x)->processor == processor) {
2060 if (x == processor_automation.end()) {
2062 rai = new ProcessorAutomationInfo (processor);
2063 processor_automation.push_back (rai);
2071 /* any older menu was deleted at the top of processors_changed()
2072 when we cleared the subplugin menu.
2075 rai->menu = manage (new Menu);
2076 MenuList& items = rai->menu->items();
2077 rai->menu->set_name ("ArdourContextMenu");
2081 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2083 ProcessorAutomationNode* pan;
2084 CheckMenuItem* mitem;
2086 string name = processor->describe_parameter (*i);
2088 items.push_back (CheckMenuElem (name));
2089 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2091 _subplugin_menu_map[*i] = mitem;
2093 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2094 mitem->set_active(true);
2097 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2101 pan = new ProcessorAutomationNode (*i, mitem, *this);
2103 rai->lines.push_back (pan);
2107 pan->menu_item = mitem;
2111 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2114 /* add the menu for this processor, because the subplugin
2115 menu is always cleared at the top of processors_changed().
2116 this is the result of some poor design in gtkmm and/or
2120 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2125 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2126 RouteTimeAxisView::ProcessorAutomationNode* pan)
2128 bool showit = pan->menu_item->get_active();
2129 bool redraw = false;
2131 if (pan->view == 0 && showit) {
2132 add_processor_automation_curve (rai->processor, pan->what);
2136 if (pan->view && showit != pan->view->marked_for_display()) {
2139 pan->view->set_marked_for_display (true);
2140 pan->view->canvas_display()->show();
2141 pan->view->canvas_background()->show();
2143 rai->processor->mark_automation_visible (pan->what, true);
2144 pan->view->set_marked_for_display (false);
2152 if (redraw && !no_redraw) {
2153 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2159 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2161 if (c.type == RouteProcessorChange::MeterPointChange) {
2162 /* nothing to do if only the meter point has changed */
2166 using namespace Menu_Helpers;
2168 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2169 (*i)->valid = false;
2172 _subplugin_menu_map.clear ();
2173 subplugin_menu.items().clear ();
2175 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2176 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2178 bool deleted_processor_automation = false;
2180 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2182 list<ProcessorAutomationInfo*>::iterator tmp;
2190 processor_automation.erase (i);
2191 deleted_processor_automation = true;
2198 if (deleted_processor_automation && !no_redraw) {
2199 _route->gui_changed ("track_height", this);
2203 boost::shared_ptr<AutomationLine>
2204 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2206 ProcessorAutomationNode* pan;
2208 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2214 return boost::shared_ptr<AutomationLine>();
2218 RouteTimeAxisView::reset_processor_automation_curves ()
2220 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2226 RouteTimeAxisView::update_rec_display ()
2228 RouteUI::update_rec_display ();
2229 name_entry.set_sensitive (!_route->record_enabled());
2233 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2235 if (apply_to_selection) {
2236 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2240 _view->set_layer_display (d);
2244 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2249 RouteTimeAxisView::layer_display () const
2252 return _view->layer_display ();
2255 /* we don't know, since we don't have a _view, so just return something */
2261 boost::shared_ptr<AutomationTimeAxisView>
2262 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2264 AutomationTracks::iterator i = _automation_tracks.find(param);
2265 if (i != _automation_tracks.end()) {
2268 return boost::shared_ptr<AutomationTimeAxisView>();
2273 RouteTimeAxisView::fast_update ()
2275 gm.get_level_meter().update_meters ();
2279 RouteTimeAxisView::hide_meter ()
2282 gm.get_level_meter().hide_meters ();
2286 RouteTimeAxisView::show_meter ()
2292 RouteTimeAxisView::reset_meter ()
2294 if (Config->get_show_track_meters()) {
2295 gm.get_level_meter().setup_meters (height-5);
2302 RouteTimeAxisView::clear_meter ()
2304 gm.get_level_meter().clear_meters ();
2308 RouteTimeAxisView::meter_changed ()
2310 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2315 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2321 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2323 using namespace Menu_Helpers;
2325 if (!_underlay_streams.empty()) {
2326 MenuList& parent_items = parent_menu->items();
2327 Menu* gs_menu = manage (new Menu);
2328 gs_menu->set_name ("ArdourContextMenu");
2329 MenuList& gs_items = gs_menu->items();
2331 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2333 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2334 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2335 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2341 RouteTimeAxisView::set_underlay_state()
2343 if (!underlay_xml_node) {
2347 XMLNodeList nlist = underlay_xml_node->children();
2348 XMLNodeConstIterator niter;
2349 XMLNode *child_node;
2351 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2352 child_node = *niter;
2354 if (child_node->name() != "Underlay") {
2358 XMLProperty* prop = child_node->property ("id");
2360 PBD::ID id (prop->value());
2362 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2365 add_underlay(v->view(), false);
2374 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2380 RouteTimeAxisView& other = v->trackview();
2382 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2383 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2384 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2388 _underlay_streams.push_back(v);
2389 other._underlay_mirrors.push_back(this);
2391 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2394 if (!underlay_xml_node) {
2396 underlay_xml_node = xml_node->add_child("Underlays");
2399 XMLNode* node = underlay_xml_node->add_child("Underlay");
2400 XMLProperty* prop = node->add_property("id");
2401 prop->set_value(v->trackview().route()->id().to_s());
2407 RouteTimeAxisView::remove_underlay (StreamView* v)
2413 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2414 RouteTimeAxisView& other = v->trackview();
2416 if (it != _underlay_streams.end()) {
2417 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2419 if (gm == other._underlay_mirrors.end()) {
2420 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2424 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2426 _underlay_streams.erase(it);
2427 other._underlay_mirrors.erase(gm);
2429 if (underlay_xml_node) {
2430 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2436 RouteTimeAxisView::set_button_names ()
2438 rec_enable_button_label.set_text (_("r"));
2440 if (_route && _route->solo_safe()) {
2441 solo_button_label.set_text (X_("!"));
2443 if (Config->get_solo_control_is_listen_control()) {
2444 switch (Config->get_listen_position()) {
2445 case AfterFaderListen:
2446 solo_button_label.set_text (_("A"));
2448 case PreFaderListen:
2449 solo_button_label.set_text (_("P"));
2453 solo_button_label.set_text (_("s"));
2456 mute_button_label.set_text (_("m"));
2460 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2462 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2463 if (i != _main_automation_menu_map.end()) {
2467 i = _subplugin_menu_map.find (param);
2468 if (i != _subplugin_menu_map.end()) {
2476 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2478 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2480 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2484 gain_track.reset (new AutomationTimeAxisView (_session,
2485 _route, _route->amp(), c, param,
2490 _route->amp()->describe_parameter(param)));
2493 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2496 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);