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)) {
866 controls_table.set_border_width (2);
872 gm.get_gain_slider().show();
874 if (!_route || _route->is_monitor()) {
879 if (rec_enable_button)
880 rec_enable_button->show();
882 route_group_button.show();
883 automation_button.show();
885 if (is_track() && track()->mode() == ARDOUR::Normal) {
886 playlist_button.show();
889 } else if (height >= preset_height (HeightSmaller)) {
891 controls_table.set_border_width (2);
897 gm.get_gain_slider().hide();
899 if (!_route || _route->is_monitor()) {
904 if (rec_enable_button)
905 rec_enable_button->show();
907 route_group_button.hide ();
908 automation_button.hide ();
910 if (is_track() && track()->mode() == ARDOUR::Normal) {
911 playlist_button.hide ();
916 controls_table.set_border_width (0);
920 if (height_changed && !no_redraw) {
921 /* only emit the signal if the height really changed */
922 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
927 RouteTimeAxisView::set_color (Gdk::Color const & c)
929 RouteUI::set_color (c);
932 _view->apply_color (_color, StreamView::RegionColor);
937 RouteTimeAxisView::reset_samples_per_unit ()
939 set_samples_per_unit (_editor.get_current_zoom());
943 RouteTimeAxisView::horizontal_position_changed ()
946 _view->horizontal_position_changed ();
951 RouteTimeAxisView::set_samples_per_unit (double spu)
956 speed = track()->speed();
960 _view->set_samples_per_unit (spu * speed);
963 TimeAxisView::set_samples_per_unit (spu * speed);
967 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
969 if (!mitem->get_active()) {
970 /* this is one of the two calls made when these radio menu items change status. this one
971 is for the item that became inactive, and we want to ignore it.
976 if (apply_to_selection) {
977 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
980 track()->set_align_choice (choice);
986 RouteTimeAxisView::rename_current_playlist ()
988 ArdourPrompter prompter (true);
991 boost::shared_ptr<Track> tr = track();
992 if (!tr || tr->destructive()) {
996 boost::shared_ptr<Playlist> pl = tr->playlist();
1001 prompter.set_title (_("Rename Playlist"));
1002 prompter.set_prompt (_("New name for playlist:"));
1003 prompter.set_initial_text (pl->name());
1004 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1005 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1007 switch (prompter.run ()) {
1008 case Gtk::RESPONSE_ACCEPT:
1009 prompter.get_result (name);
1010 if (name.length()) {
1011 pl->set_name (name);
1021 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1023 std::string ret (basename);
1025 std::string const group_string = "." + route_group()->name() + ".";
1027 // iterate through all playlists
1029 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1030 std::string tmp = (*i)->name();
1032 std::string::size_type idx = tmp.find(group_string);
1033 // find those which belong to this group
1034 if (idx != string::npos) {
1035 tmp = tmp.substr(idx + group_string.length());
1037 // and find the largest current number
1038 int x = atoi(tmp.c_str());
1039 if (x > maxnumber) {
1048 snprintf (buf, sizeof(buf), "%d", maxnumber);
1050 ret = this->name() + "." + route_group()->name () + "." + buf;
1056 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1060 boost::shared_ptr<Track> tr = track ();
1061 if (!tr || tr->destructive()) {
1065 boost::shared_ptr<const Playlist> pl = tr->playlist();
1072 if (route_group() && route_group()->is_active()) {
1073 name = resolve_new_group_playlist_name(name, playlists_before_op);
1076 while (_session->playlists->by_name(name)) {
1077 name = Playlist::bump_name (name, *_session);
1080 // TODO: The prompter "new" button should be de-activated if the user
1081 // specifies a playlist name which already exists in the session.
1085 ArdourPrompter prompter (true);
1087 prompter.set_title (_("New Copy Playlist"));
1088 prompter.set_prompt (_("Name for new playlist:"));
1089 prompter.set_initial_text (name);
1090 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1091 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1092 prompter.show_all ();
1094 switch (prompter.run ()) {
1095 case Gtk::RESPONSE_ACCEPT:
1096 prompter.get_result (name);
1104 if (name.length()) {
1105 tr->use_copy_playlist ();
1106 tr->playlist()->set_name (name);
1111 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1115 boost::shared_ptr<Track> tr = track ();
1116 if (!tr || tr->destructive()) {
1120 boost::shared_ptr<const Playlist> pl = tr->playlist();
1127 if (route_group() && route_group()->is_active()) {
1128 name = resolve_new_group_playlist_name(name,playlists_before_op);
1131 while (_session->playlists->by_name(name)) {
1132 name = Playlist::bump_name (name, *_session);
1138 ArdourPrompter prompter (true);
1140 prompter.set_title (_("New Playlist"));
1141 prompter.set_prompt (_("Name for new playlist:"));
1142 prompter.set_initial_text (name);
1143 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1144 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1146 switch (prompter.run ()) {
1147 case Gtk::RESPONSE_ACCEPT:
1148 prompter.get_result (name);
1156 if (name.length()) {
1157 tr->use_new_playlist ();
1158 tr->playlist()->set_name (name);
1163 RouteTimeAxisView::clear_playlist ()
1165 boost::shared_ptr<Track> tr = track ();
1166 if (!tr || tr->destructive()) {
1170 boost::shared_ptr<Playlist> pl = tr->playlist();
1175 _editor.clear_playlist (pl);
1179 RouteTimeAxisView::speed_changed ()
1181 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1185 RouteTimeAxisView::update_diskstream_display ()
1195 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1197 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1199 /* special case: select/deselect all tracks */
1200 if (_editor.get_selection().selected (this)) {
1201 _editor.get_selection().clear_tracks ();
1203 _editor.select_all_tracks ();
1209 switch (ArdourKeyboard::selection_type (ev->state)) {
1210 case Selection::Toggle:
1211 _editor.get_selection().toggle (this);
1214 case Selection::Set:
1215 _editor.get_selection().set (this);
1218 case Selection::Extend:
1219 _editor.extend_selection_to_track (*this);
1222 case Selection::Add:
1223 _editor.get_selection().add (this);
1229 RouteTimeAxisView::set_selected_points (PointSelection& points)
1231 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1232 (*i)->set_selected_points (points);
1237 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1240 _view->set_selected_regionviews (regions);
1244 /** Add the selectable things that we have to a list.
1245 * @param results List to add things to.
1248 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1253 speed = track()->speed();
1256 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1257 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1259 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1260 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1263 /* pick up visible automation tracks */
1265 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1266 if (!(*i)->hidden()) {
1267 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1273 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1276 _view->get_inverted_selectables (sel, results);
1279 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1280 if (!(*i)->hidden()) {
1281 (*i)->get_inverted_selectables (sel, results);
1289 RouteTimeAxisView::route_group () const
1291 return _route->route_group();
1295 RouteTimeAxisView::name() const
1297 return _route->name();
1300 boost::shared_ptr<Playlist>
1301 RouteTimeAxisView::playlist () const
1303 boost::shared_ptr<Track> tr;
1305 if ((tr = track()) != 0) {
1306 return tr->playlist();
1308 return boost::shared_ptr<Playlist> ();
1313 RouteTimeAxisView::name_entry_changed ()
1317 x = name_entry.get_text ();
1319 if (x == _route->name()) {
1323 strip_whitespace_edges(x);
1325 if (x.length() == 0) {
1326 name_entry.set_text (_route->name());
1330 if (!_session->route_name_unique (x)) {
1331 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1332 name_entry.set_text (_route->name());
1333 } else if (_session->route_name_internal (x)) {
1334 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1336 name_entry.set_text (_route->name());
1338 _route->set_name (x);
1342 boost::shared_ptr<Region>
1343 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1345 boost::shared_ptr<Playlist> pl = playlist ();
1348 return pl->find_next_region (pos, point, dir);
1351 return boost::shared_ptr<Region> ();
1355 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1357 boost::shared_ptr<Playlist> pl = playlist ();
1360 return pl->find_next_region_boundary (pos, dir);
1367 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1369 boost::shared_ptr<Playlist> what_we_got;
1370 boost::shared_ptr<Track> tr = track ();
1371 boost::shared_ptr<Playlist> playlist;
1374 /* route is a bus, not a track */
1378 playlist = tr->playlist();
1380 TimeSelection time (selection.time);
1381 float const speed = tr->speed();
1382 if (speed != 1.0f) {
1383 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1384 (*i).start = session_frame_to_track_frame((*i).start, speed);
1385 (*i).end = session_frame_to_track_frame((*i).end, speed);
1389 playlist->clear_changes ();
1390 playlist->clear_owned_changes ();
1394 if ((what_we_got = playlist->cut (time)) != 0) {
1395 _editor.get_cut_buffer().add (what_we_got);
1397 vector<Command*> cmds;
1398 playlist->rdiff (cmds);
1399 _session->add_commands (cmds);
1401 _session->add_command (new StatefulDiffCommand (playlist));
1405 if ((what_we_got = playlist->copy (time)) != 0) {
1406 _editor.get_cut_buffer().add (what_we_got);
1411 if ((what_we_got = playlist->cut (time)) != 0) {
1413 vector<Command*> cmds;
1414 playlist->rdiff (cmds);
1415 _session->add_commands (cmds);
1416 _session->add_command (new StatefulDiffCommand (playlist));
1417 what_we_got->release ();
1424 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1430 boost::shared_ptr<Playlist> pl = playlist ();
1431 PlaylistSelection::iterator p;
1433 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1435 if (p == selection.playlists.end()) {
1439 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1441 if (track()->speed() != 1.0f) {
1442 pos = session_frame_to_track_frame (pos, track()->speed());
1443 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1446 pl->clear_changes ();
1447 pl->paste (*p, pos, times);
1448 _session->add_command (new StatefulDiffCommand (pl));
1454 struct PlaylistSorter {
1455 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1456 return a->sort_id() < b->sort_id();
1461 RouteTimeAxisView::build_playlist_menu ()
1463 using namespace Menu_Helpers;
1469 delete playlist_action_menu;
1470 playlist_action_menu = new Menu;
1471 playlist_action_menu->set_name ("ArdourContextMenu");
1473 MenuList& playlist_items = playlist_action_menu->items();
1474 playlist_action_menu->set_name ("ArdourContextMenu");
1475 playlist_items.clear();
1477 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1478 boost::shared_ptr<Track> tr = track();
1479 RadioMenuItem::Group playlist_group;
1481 _session->playlists->get (playlists);
1483 /* find the playlists for this diskstream */
1484 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1485 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1486 playlists_tr.push_back(*i);
1490 /* sort the playlists */
1492 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1494 /* add the playlists to the menu */
1495 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1496 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1497 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1498 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1500 if (tr->playlist()->id() == (*i)->id()) {
1506 playlist_items.push_back (SeparatorElem());
1507 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1508 playlist_items.push_back (SeparatorElem());
1510 if (!route_group() || !route_group()->is_active()) {
1511 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1512 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1515 // Use a label which tells the user what is happening
1516 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1517 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1521 playlist_items.push_back (SeparatorElem());
1522 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1523 playlist_items.push_back (SeparatorElem());
1525 playlist_items.push_back (MenuElem(_("Select from all..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1529 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1531 assert (is_track());
1533 // exit if we were triggered by deactivating the old playlist
1534 if (!item->get_active()) {
1538 boost::shared_ptr<Playlist> pl (wpl.lock());
1544 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1547 if (track()->playlist() == apl) {
1548 // exit when use_playlist is called by the creation of the playlist menu
1549 // or the playlist choice is unchanged
1552 track()->use_playlist (apl);
1554 if (route_group() && route_group()->is_active()) {
1555 std::string group_string = "."+route_group()->name()+".";
1557 std::string take_name = apl->name();
1558 std::string::size_type idx = take_name.find(group_string);
1560 if (idx == std::string::npos)
1563 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1565 boost::shared_ptr<RouteList> rl (route_group()->route_list());
1567 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1568 if ( (*i) == this->route()) {
1572 std::string playlist_name = (*i)->name()+group_string+take_name;
1574 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1579 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1581 // No playlist for this track for this take yet, make it
1582 track->use_new_playlist();
1583 track->playlist()->set_name(playlist_name);
1585 track->use_playlist(ipl);
1593 RouteTimeAxisView::show_playlist_selector ()
1595 _editor.playlist_selector().show_for (this);
1599 RouteTimeAxisView::map_frozen ()
1605 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1607 switch (track()->freeze_state()) {
1609 playlist_button.set_sensitive (false);
1610 rec_enable_button->set_sensitive (false);
1613 playlist_button.set_sensitive (true);
1614 rec_enable_button->set_sensitive (true);
1620 RouteTimeAxisView::color_handler ()
1622 //case cTimeStretchOutline:
1623 if (timestretch_rect) {
1624 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1626 //case cTimeStretchFill:
1627 if (timestretch_rect) {
1628 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1634 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1635 * Will add track if necessary.
1638 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1640 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1641 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1644 /* it doesn't exist yet, so we don't care about the button state: just add it */
1645 create_automation_child (param, true);
1648 bool yn = menu->get_active();
1649 if (track->set_visibility (menu->get_active()) && yn) {
1651 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1652 will have done that for us.
1656 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1663 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1665 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1671 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1673 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1674 track->get_state_node()->add_property (X_("shown"), X_("no"));
1676 if (menu && !_hidden) {
1677 ignore_toggle = true;
1678 menu->set_active (false);
1679 ignore_toggle = false;
1682 if (_route && !no_redraw) {
1683 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1689 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1691 if (apply_to_selection) {
1692 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1696 /* Show our automation */
1698 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1699 i->second->set_marked_for_display (true);
1700 i->second->canvas_display()->show();
1701 i->second->get_state_node()->add_property ("shown", X_("yes"));
1703 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1706 menu->set_active(true);
1711 /* Show processor automation */
1713 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1714 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1715 if ((*ii)->view == 0) {
1716 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1719 (*ii)->menu_item->set_active (true);
1727 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1732 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1734 if (apply_to_selection) {
1735 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1739 /* Show our automation */
1741 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1742 if (i->second->has_automation()) {
1743 i->second->set_marked_for_display (true);
1744 i->second->canvas_display()->show();
1745 i->second->get_state_node()->add_property ("shown", X_("yes"));
1747 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1749 menu->set_active(true);
1755 /* Show processor automation */
1757 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1758 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1759 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1760 (*ii)->menu_item->set_active (true);
1767 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1772 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1774 if (apply_to_selection) {
1775 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1779 /* Hide our automation */
1781 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1782 i->second->set_marked_for_display (false);
1784 i->second->get_state_node()->add_property ("shown", X_("no"));
1786 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1789 menu->set_active (false);
1793 /* Hide processor automation */
1795 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1796 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1797 (*ii)->menu_item->set_active (false);
1802 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1808 RouteTimeAxisView::region_view_added (RegionView* rv)
1810 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1811 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1812 boost::shared_ptr<AutomationTimeAxisView> atv;
1814 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1819 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1820 (*i)->add_ghost(rv);
1824 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1826 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1832 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1834 parent.remove_processor_automation_node (this);
1838 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1841 remove_child (pan->view);
1845 RouteTimeAxisView::ProcessorAutomationNode*
1846 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1848 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1850 if ((*i)->processor == processor) {
1852 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1853 if ((*ii)->what == what) {
1864 legalize_for_xml_node (string str)
1866 string::size_type pos;
1867 string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1873 while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1874 legal.replace (pos, 1, "_");
1883 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1886 ProcessorAutomationNode* pan;
1888 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1889 /* session state may never have been saved with new plugin */
1890 error << _("programming error: ")
1891 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1892 processor->name(), what.type(), (int) what.channel(), what.id() )
1902 name = processor->describe_parameter (what);
1904 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1908 char state_name[256];
1909 snprintf (state_name, sizeof (state_name), "%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1911 boost::shared_ptr<AutomationControl> control
1912 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1914 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1915 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1916 _editor, *this, false, parent_canvas, name, state_name));
1918 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1920 if (!pan->view->marked_for_display()) {
1923 pan->menu_item->set_active (true);
1926 add_child (pan->view);
1929 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1932 processor->mark_automation_visible (what, true);
1936 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1939 pan->menu_item->set_active (false);
1942 i->mark_automation_visible (pan->what, false);
1945 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1950 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1952 boost::shared_ptr<Processor> processor (p.lock ());
1957 set<Evoral::Parameter> s;
1958 boost::shared_ptr<AutomationLine> al;
1960 processor->what_has_visible_data (s);
1962 for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1964 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1967 add_processor_automation_curve (processor, (*i));
1973 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1975 using namespace Menu_Helpers;
1982 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1984 bool hideit = (!show);
1986 if ((node = track->get_state_node()) != 0) {
1987 if ((prop = node->property ("shown")) != 0) {
1988 if (string_is_affirmative (prop->value())) {
1994 _automation_tracks[param] = track;
1996 track->set_visibility (!hideit);
1999 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2002 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2003 /* MIDI-related parameters are always in the menu, there's no
2004 reason to rebuild the menu just because we added a automation
2005 lane for one of them. But if we add a non-MIDI automation
2006 lane, then we need to invalidate the display menu.
2008 delete display_menu;
2014 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2016 boost::shared_ptr<Processor> processor (p.lock ());
2018 if (!processor || !processor->display_to_user ()) {
2022 using namespace Menu_Helpers;
2023 ProcessorAutomationInfo *rai;
2024 list<ProcessorAutomationInfo*>::iterator x;
2026 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2027 std::set<Evoral::Parameter> has_visible_automation;
2029 processor->what_has_visible_data(has_visible_automation);
2031 if (automatable.empty()) {
2035 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2036 if ((*x)->processor == processor) {
2041 if (x == processor_automation.end()) {
2043 rai = new ProcessorAutomationInfo (processor);
2044 processor_automation.push_back (rai);
2052 /* any older menu was deleted at the top of processors_changed()
2053 when we cleared the subplugin menu.
2056 rai->menu = manage (new Menu);
2057 MenuList& items = rai->menu->items();
2058 rai->menu->set_name ("ArdourContextMenu");
2062 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2064 ProcessorAutomationNode* pan;
2065 CheckMenuItem* mitem;
2067 string name = processor->describe_parameter (*i);
2069 items.push_back (CheckMenuElem (name));
2070 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2072 _subplugin_menu_map[*i] = mitem;
2074 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2075 mitem->set_active(true);
2078 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2082 pan = new ProcessorAutomationNode (*i, mitem, *this);
2084 rai->lines.push_back (pan);
2088 pan->menu_item = mitem;
2092 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2095 /* add the menu for this processor, because the subplugin
2096 menu is always cleared at the top of processors_changed().
2097 this is the result of some poor design in gtkmm and/or
2101 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2106 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2107 RouteTimeAxisView::ProcessorAutomationNode* pan)
2109 bool showit = pan->menu_item->get_active();
2110 bool redraw = false;
2112 if (pan->view == 0 && showit) {
2113 add_processor_automation_curve (rai->processor, pan->what);
2117 if (pan->view && showit != pan->view->marked_for_display()) {
2120 pan->view->set_marked_for_display (true);
2121 pan->view->canvas_display()->show();
2122 pan->view->canvas_background()->show();
2124 rai->processor->mark_automation_visible (pan->what, true);
2125 pan->view->set_marked_for_display (false);
2133 if (redraw && !no_redraw) {
2134 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2140 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2142 if (c.type == RouteProcessorChange::MeterPointChange) {
2143 /* nothing to do if only the meter point has changed */
2147 using namespace Menu_Helpers;
2149 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2150 (*i)->valid = false;
2153 _subplugin_menu_map.clear ();
2154 subplugin_menu.items().clear ();
2156 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2157 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2159 bool deleted_processor_automation = false;
2161 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2163 list<ProcessorAutomationInfo*>::iterator tmp;
2171 processor_automation.erase (i);
2172 deleted_processor_automation = true;
2179 if (deleted_processor_automation && !no_redraw) {
2180 _route->gui_changed ("track_height", this);
2184 boost::shared_ptr<AutomationLine>
2185 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2187 ProcessorAutomationNode* pan;
2189 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2195 return boost::shared_ptr<AutomationLine>();
2199 RouteTimeAxisView::reset_processor_automation_curves ()
2201 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2207 RouteTimeAxisView::update_rec_display ()
2209 RouteUI::update_rec_display ();
2210 name_entry.set_sensitive (!_route->record_enabled());
2214 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2216 if (apply_to_selection) {
2217 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2221 _view->set_layer_display (d);
2225 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2230 RouteTimeAxisView::layer_display () const
2233 return _view->layer_display ();
2236 /* we don't know, since we don't have a _view, so just return something */
2242 boost::shared_ptr<AutomationTimeAxisView>
2243 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2245 AutomationTracks::iterator i = _automation_tracks.find(param);
2246 if (i != _automation_tracks.end()) {
2249 return boost::shared_ptr<AutomationTimeAxisView>();
2254 RouteTimeAxisView::fast_update ()
2256 gm.get_level_meter().update_meters ();
2260 RouteTimeAxisView::hide_meter ()
2263 gm.get_level_meter().hide_meters ();
2267 RouteTimeAxisView::show_meter ()
2273 RouteTimeAxisView::reset_meter ()
2275 if (Config->get_show_track_meters()) {
2276 gm.get_level_meter().setup_meters (height-5);
2283 RouteTimeAxisView::clear_meter ()
2285 gm.get_level_meter().clear_meters ();
2289 RouteTimeAxisView::meter_changed ()
2291 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2296 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2302 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2304 using namespace Menu_Helpers;
2306 if (!_underlay_streams.empty()) {
2307 MenuList& parent_items = parent_menu->items();
2308 Menu* gs_menu = manage (new Menu);
2309 gs_menu->set_name ("ArdourContextMenu");
2310 MenuList& gs_items = gs_menu->items();
2312 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2314 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2315 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2316 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2322 RouteTimeAxisView::set_underlay_state()
2324 if (!underlay_xml_node) {
2328 XMLNodeList nlist = underlay_xml_node->children();
2329 XMLNodeConstIterator niter;
2330 XMLNode *child_node;
2332 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2333 child_node = *niter;
2335 if (child_node->name() != "Underlay") {
2339 XMLProperty* prop = child_node->property ("id");
2341 PBD::ID id (prop->value());
2343 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2346 add_underlay(v->view(), false);
2355 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2361 RouteTimeAxisView& other = v->trackview();
2363 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2364 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2365 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2369 _underlay_streams.push_back(v);
2370 other._underlay_mirrors.push_back(this);
2372 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2375 if (!underlay_xml_node) {
2377 underlay_xml_node = xml_node->add_child("Underlays");
2380 XMLNode* node = underlay_xml_node->add_child("Underlay");
2381 XMLProperty* prop = node->add_property("id");
2382 prop->set_value(v->trackview().route()->id().to_s());
2388 RouteTimeAxisView::remove_underlay (StreamView* v)
2394 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2395 RouteTimeAxisView& other = v->trackview();
2397 if (it != _underlay_streams.end()) {
2398 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2400 if (gm == other._underlay_mirrors.end()) {
2401 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2405 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2407 _underlay_streams.erase(it);
2408 other._underlay_mirrors.erase(gm);
2410 if (underlay_xml_node) {
2411 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2417 RouteTimeAxisView::set_button_names ()
2419 rec_enable_button_label.set_text (_("r"));
2421 if (_route && _route->solo_safe()) {
2422 solo_button_label.set_text (X_("!"));
2424 if (Config->get_solo_control_is_listen_control()) {
2425 switch (Config->get_listen_position()) {
2426 case AfterFaderListen:
2427 solo_button_label.set_text (_("A"));
2429 case PreFaderListen:
2430 solo_button_label.set_text (_("P"));
2434 solo_button_label.set_text (_("s"));
2437 mute_button_label.set_text (_("m"));
2441 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2443 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2444 if (i != _main_automation_menu_map.end()) {
2448 i = _subplugin_menu_map.find (param);
2449 if (i != _subplugin_menu_map.end()) {
2457 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2459 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2461 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2465 gain_track.reset (new AutomationTimeAxisView (_session,
2466 _route, _route->amp(), c, param,
2471 _route->amp()->describe_parameter(param)));
2474 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2477 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);