Fix corruption of follow playhead state on quit (#4048).
[ardour2.git] / gtk2_ardour / route_time_axis.cc
blob2dbc8635f73a6fbadc9b6d095a4deacc4bec9a13
1 /*
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.
19 #include <cstdlib>
20 #include <cmath>
21 #include <cassert>
23 #include <algorithm>
24 #include <string>
25 #include <vector>
26 #include <map>
27 #include <utility>
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"
64 #include "debug.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"
70 #include "enums.h"
71 #include "gui_thread.h"
72 #include "keyboard.h"
73 #include "playlist_selector.h"
74 #include "point_selection.h"
75 #include "prompter.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"
82 #include "utils.h"
83 #include "route_group_menu.h"
85 #include "ardour/track.h"
87 #include "i18n.h"
89 using namespace ARDOUR;
90 using namespace PBD;
91 using namespace Gtkmm2ext;
92 using namespace Gtk;
93 using namespace Editing;
94 using namespace std;
96 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
98 void
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)
107 : AxisView(sess)
108 , RouteUI(rt, sess)
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);
121 _has_state = true;
122 playlist_action_menu = 0;
123 automation_action_menu = 0;
124 plugins_submenu_item = 0;
125 mode_menu = 0;
126 _view = 0;
128 if (!_route->is_hidden()) {
129 _marked_for_display = true;
132 mute_changed (0);
133 update_solo_display ();
135 timestretch_rect = 0;
136 no_redraw = false;
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));
152 if (is_track()) {
154 /* use icon */
156 rec_enable_button->remove ();
158 switch (track()->mode()) {
159 case ARDOUR::Normal:
160 case ARDOUR::NonLayered:
161 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
162 break;
163 case ARDOUR::Destructive:
164 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
165 break;
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)"));
173 } else {
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"));
200 label_view ();
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);
208 _y_position = -1;
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());
213 if (is_track()) {
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 */
219 map_frozen ();
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");
238 show_name_entry ();
239 hide_name_label ();
242 RouteTimeAxisView::~RouteTimeAxisView ()
244 CatchDeletion (this);
246 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
247 delete *i;
250 delete playlist_action_menu;
251 playlist_action_menu = 0;
253 delete _view;
254 _view = 0;
256 _automation_tracks.clear ();
258 delete route_group_menu;
261 void
262 RouteTimeAxisView::post_construct ()
264 /* map current state of the route */
266 update_diskstream_display ();
268 _subplugin_menu_map.clear ();
269 subplugin_menu.items().clear ();
270 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
271 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
272 reset_processor_automation_curves ();
275 gint
276 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
278 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
279 if (_route->route_group()) {
280 _route->route_group()->remove (_route);
282 return false;
285 WeakRouteList r;
286 r.push_back (route ());
288 route_group_menu->build (r);
289 route_group_menu->menu()->popup (ev->button, ev->time);
291 return false;
294 void
295 RouteTimeAxisView::playlist_changed ()
297 label_view ();
300 void
301 RouteTimeAxisView::label_view ()
303 string x = _route->name();
305 if (x != name_entry.get_text()) {
306 name_entry.set_text (x);
309 if (x != name_label.get_text()) {
310 name_label.set_text (x);
313 ARDOUR_UI::instance()->set_tip (name_entry, x);
316 void
317 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
319 if (what_changed.contains (ARDOUR::Properties::name)) {
320 label_view ();
324 void
325 RouteTimeAxisView::take_name_changed (void *src)
327 if (src != this) {
328 label_view ();
332 void
333 RouteTimeAxisView::playlist_click ()
335 build_playlist_menu ();
336 conditionally_add_to_selection ();
337 playlist_action_menu->popup (1, gtk_get_current_event_time());
340 void
341 RouteTimeAxisView::automation_click ()
343 conditionally_add_to_selection ();
344 build_automation_action_menu (false);
345 automation_action_menu->popup (1, gtk_get_current_event_time());
349 RouteTimeAxisView::set_state (const XMLNode& node, int version)
351 TimeAxisView::set_state (node, version);
353 XMLNodeList kids = node.children();
354 XMLNodeConstIterator iter;
355 const XMLProperty* prop;
357 if (_view && (prop = node.property ("layer-display"))) {
358 set_layer_display (LayerDisplay (string_2_enum (prop->value(), _view->layer_display ())));
361 for (iter = kids.begin(); iter != kids.end(); ++iter) {
362 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
363 if ((prop = (*iter)->property ("automation-id")) != 0) {
365 Evoral::Parameter param = ARDOUR::EventTypeMap::instance().new_parameter(prop->value());
366 bool show = ((prop = (*iter)->property ("shown")) != 0) && string_is_affirmative (prop->value());
367 create_automation_child(param, show);
368 } else {
369 warning << "Automation child has no ID" << endmsg;
374 return 0;
377 void
378 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
380 using namespace Menu_Helpers;
382 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
383 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
386 detach_menu (subplugin_menu);
388 _main_automation_menu_map.clear ();
389 delete automation_action_menu;
390 automation_action_menu = new Menu;
392 MenuList& items = automation_action_menu->items();
394 automation_action_menu->set_name ("ArdourContextMenu");
396 items.push_back (MenuElem (_("Show All Automation"),
397 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
399 items.push_back (MenuElem (_("Show Existing Automation"),
400 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
402 items.push_back (MenuElem (_("Hide All Automation"),
403 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
405 items.push_back (SeparatorElem ());
407 /* Attach the plugin submenu. It may have previously been used elsewhere,
408 so it was detached above */
410 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
411 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
414 void
415 RouteTimeAxisView::build_display_menu ()
417 using namespace Menu_Helpers;
419 /* prepare it */
421 TimeAxisView::build_display_menu ();
423 /* now fill it with our stuff */
425 MenuList& items = display_menu->items();
426 display_menu->set_name ("ArdourContextMenu");
428 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
430 if (_size_menu) {
431 detach_menu (*_size_menu);
433 build_size_menu ();
434 items.push_back (MenuElem (_("Height"), *_size_menu));
436 items.push_back (SeparatorElem());
438 if (!Profile->get_sae()) {
439 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
440 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
441 items.push_back (SeparatorElem());
444 // Hook for derived classes to add type specific stuff
445 append_extra_display_menu_items ();
447 if (is_track()) {
449 Menu* layers_menu = manage (new Menu);
450 MenuList &layers_items = layers_menu->items();
451 layers_menu->set_name("ArdourContextMenu");
453 RadioMenuItem::Group layers_group;
455 /* Find out how many overlaid/stacked tracks we have in the selection */
457 int overlaid = 0;
458 int stacked = 0;
459 TrackSelection const & s = _editor.get_selection().tracks;
460 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
461 StreamView* v = (*i)->view ();
462 if (!v) {
463 continue;
466 switch (v->layer_display ()) {
467 case Overlaid:
468 ++overlaid;
469 break;
470 case Stacked:
471 ++stacked;
472 break;
476 /* We're not connecting to signal_toggled() here; in the case where these two items are
477 set to be in the `inconsistent' state, it seems that one or other will end up active
478 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
479 select the active one, no toggled signal is emitted so nothing happens.
482 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
483 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
484 i->set_active (overlaid != 0 && stacked == 0);
485 i->set_inconsistent (overlaid != 0 && stacked != 0);
486 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
488 layers_items.push_back (
489 RadioMenuElem (layers_group, _("Stacked"),
490 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
493 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
494 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
495 i->set_active (overlaid == 0 && stacked != 0);
496 i->set_inconsistent (overlaid != 0 && stacked != 0);
498 items.push_back (MenuElem (_("Layers"), *layers_menu));
500 if (!Profile->get_sae()) {
502 Menu* alignment_menu = manage (new Menu);
503 MenuList& alignment_items = alignment_menu->items();
504 alignment_menu->set_name ("ArdourContextMenu");
506 RadioMenuItem::Group align_group;
508 /* Same verbose hacks as for the layering options above */
510 int existing = 0;
511 int capture = 0;
512 int automatic = 0;
513 int styles = 0;
514 boost::shared_ptr<Track> first_track;
516 TrackSelection const & s = _editor.get_selection().tracks;
517 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
518 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
519 if (!r || !r->is_track ()) {
520 continue;
523 if (!first_track) {
524 first_track = r->track();
527 switch (r->track()->alignment_choice()) {
528 case Automatic:
529 ++automatic;
530 styles |= 0x1;
531 switch (r->track()->alignment_style()) {
532 case ExistingMaterial:
533 ++existing;
534 break;
535 case CaptureTime:
536 ++capture;
537 break;
539 break;
540 case UseExistingMaterial:
541 ++existing;
542 styles |= 0x2;
543 break;
544 case UseCaptureTime:
545 ++capture;
546 styles |= 0x4;
547 break;
551 bool inconsistent;
552 switch (styles) {
553 case 1:
554 case 2:
555 case 4:
556 inconsistent = false;
557 break;
558 default:
559 inconsistent = true;
560 break;
563 RadioMenuItem* i;
565 if (!inconsistent && first_track) {
567 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
568 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
569 i->set_active (automatic != 0 && existing == 0 && capture == 0);
570 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
572 switch (first_track->alignment_choice()) {
573 case Automatic:
574 switch (first_track->alignment_style()) {
575 case ExistingMaterial:
576 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
577 break;
578 case CaptureTime:
579 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
580 break;
582 break;
583 default:
584 break;
587 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
588 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
589 i->set_active (existing != 0 && capture == 0 && automatic == 0);
590 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
592 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
593 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
594 i->set_active (existing == 0 && capture != 0 && automatic == 0);
595 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
597 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
599 } else {
600 /* show nothing */
603 Menu* mode_menu = manage (new Menu);
604 MenuList& mode_items = mode_menu->items ();
605 mode_menu->set_name ("ArdourContextMenu");
607 RadioMenuItem::Group mode_group;
609 int normal = 0;
610 int tape = 0;
611 int non_layered = 0;
613 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
614 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
615 if (!r || !r->is_track ()) {
616 continue;
619 switch (r->track()->mode()) {
620 case Normal:
621 ++normal;
622 break;
623 case Destructive:
624 ++tape;
625 break;
626 case NonLayered:
627 ++non_layered;
628 break;
632 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
633 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
634 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
635 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
636 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
638 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
639 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
640 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
641 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
642 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
644 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
645 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
646 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
647 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
648 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
650 items.push_back (MenuElem (_("Mode"), *mode_menu));
653 color_mode_menu = build_color_mode_menu();
654 if (color_mode_menu) {
655 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
658 items.push_back (SeparatorElem());
660 build_playlist_menu ();
661 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
662 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
664 route_group_menu->detach ();
666 WeakRouteList r;
667 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
668 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
669 if (rtv) {
670 r.push_back (rtv->route ());
674 if (r.empty ()) {
675 r.push_back (route ());
678 route_group_menu->build (r);
679 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
681 build_automation_action_menu (true);
682 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
684 items.push_back (SeparatorElem());
687 int active = 0;
688 int inactive = 0;
689 TrackSelection const & s = _editor.get_selection().tracks;
690 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
691 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
692 if (!r) {
693 continue;
696 if (r->route()->active()) {
697 ++active;
698 } else {
699 ++inactive;
703 items.push_back (CheckMenuElem (_("Active")));
704 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
705 bool click_sets_active = true;
706 if (active > 0 && inactive == 0) {
707 i->set_active (true);
708 click_sets_active = false;
709 } else if (active > 0 && inactive > 0) {
710 i->set_inconsistent (true);
712 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
714 items.push_back (SeparatorElem());
715 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
716 if (!Profile->get_sae()) {
717 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
718 } else {
719 items.push_front (SeparatorElem());
720 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
724 void
725 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
727 if (apply_to_selection) {
728 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
729 } else {
731 bool needs_bounce;
733 if (!track()->can_use_mode (mode, needs_bounce)) {
735 if (!needs_bounce) {
736 /* cannot be done */
737 return;
738 } else {
739 cerr << "would bounce this one\n";
740 return;
744 track()->set_mode (mode);
746 rec_enable_button->remove ();
748 switch (mode) {
749 case ARDOUR::NonLayered:
750 case ARDOUR::Normal:
751 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
752 break;
753 case ARDOUR::Destructive:
754 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
755 break;
758 rec_enable_button->show_all ();
762 void
763 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
765 double x1;
766 double x2;
767 double y2;
769 TimeAxisView::show_timestretch (start, end);
771 hide_timestretch ();
773 #if 0
774 if (ts.empty()) {
775 return;
779 /* check that the time selection was made in our route, or our route group.
780 remember that route_group() == 0 implies the route is *not* in a edit group.
783 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
784 /* this doesn't apply to us */
785 return;
788 /* ignore it if our edit group is not active */
790 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
791 return;
793 #endif
795 if (timestretch_rect == 0) {
796 timestretch_rect = new SimpleRect (*canvas_display ());
797 timestretch_rect->property_x1() = 0.0;
798 timestretch_rect->property_y1() = 0.0;
799 timestretch_rect->property_x2() = 0.0;
800 timestretch_rect->property_y2() = 0.0;
801 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
802 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
805 timestretch_rect->show ();
806 timestretch_rect->raise_to_top ();
808 x1 = start / _editor.get_current_zoom();
809 x2 = (end - 1) / _editor.get_current_zoom();
810 y2 = current_height() - 2;
812 timestretch_rect->property_x1() = x1;
813 timestretch_rect->property_y1() = 1.0;
814 timestretch_rect->property_x2() = x2;
815 timestretch_rect->property_y2() = y2;
818 void
819 RouteTimeAxisView::hide_timestretch ()
821 TimeAxisView::hide_timestretch ();
823 if (timestretch_rect) {
824 timestretch_rect->hide ();
828 void
829 RouteTimeAxisView::show_selection (TimeSelection& ts)
832 #if 0
833 /* ignore it if our edit group is not active or if the selection was started
834 in some other track or route group (remember that route_group() == 0 means
835 that the track is not in an route group).
838 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
839 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
840 hide_selection ();
841 return;
843 #endif
845 TimeAxisView::show_selection (ts);
848 void
849 RouteTimeAxisView::set_height (uint32_t h)
851 int gmlen = h - 5;
852 bool height_changed = (height == 0) || (h != height);
853 gm.get_level_meter().setup_meters (gmlen);
855 TimeAxisView::set_height (h);
857 ensure_xml_node ();
859 if (_view) {
860 _view->set_height ((double) current_height());
863 char buf[32];
864 snprintf (buf, sizeof (buf), "%u", height);
865 xml_node->add_property ("height", buf);
867 if (height >= preset_height (HeightNormal)) {
869 _controls_padding_table.set_row_spacings (2);
871 reset_meter();
873 gm.get_gain_slider().show();
874 mute_button->show();
875 if (!_route || _route->is_monitor()) {
876 solo_button->hide();
877 } else {
878 solo_button->show();
880 if (rec_enable_button)
881 rec_enable_button->show();
883 route_group_button.show();
884 automation_button.show();
886 if (is_track() && track()->mode() == ARDOUR::Normal) {
887 playlist_button.show();
890 } else if (height >= preset_height (HeightSmaller)) {
892 _controls_padding_table.set_row_spacings (2);
894 reset_meter();
896 gm.get_gain_slider().hide();
897 mute_button->show();
898 if (!_route || _route->is_monitor()) {
899 solo_button->hide();
900 } else {
901 solo_button->show();
903 if (rec_enable_button)
904 rec_enable_button->show();
906 route_group_button.hide ();
907 automation_button.hide ();
909 if (is_track() && track()->mode() == ARDOUR::Normal) {
910 playlist_button.hide ();
913 } else {
915 _controls_padding_table.set_row_spacings (0);
919 if (height_changed && !no_redraw) {
920 /* only emit the signal if the height really changed */
921 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
925 void
926 RouteTimeAxisView::set_color (Gdk::Color const & c)
928 RouteUI::set_color (c);
930 if (_view) {
931 _view->apply_color (_color, StreamView::RegionColor);
935 void
936 RouteTimeAxisView::reset_samples_per_unit ()
938 set_samples_per_unit (_editor.get_current_zoom());
941 void
942 RouteTimeAxisView::horizontal_position_changed ()
944 if (_view) {
945 _view->horizontal_position_changed ();
949 void
950 RouteTimeAxisView::set_samples_per_unit (double spu)
952 double speed = 1.0;
954 if (track()) {
955 speed = track()->speed();
958 if (_view) {
959 _view->set_samples_per_unit (spu * speed);
962 TimeAxisView::set_samples_per_unit (spu * speed);
965 void
966 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
968 if (!mitem->get_active()) {
969 /* this is one of the two calls made when these radio menu items change status. this one
970 is for the item that became inactive, and we want to ignore it.
972 return;
975 if (apply_to_selection) {
976 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
977 } else {
978 if (track ()) {
979 track()->set_align_choice (choice);
984 void
985 RouteTimeAxisView::rename_current_playlist ()
987 ArdourPrompter prompter (true);
988 string name;
990 boost::shared_ptr<Track> tr = track();
991 if (!tr || tr->destructive()) {
992 return;
995 boost::shared_ptr<Playlist> pl = tr->playlist();
996 if (!pl) {
997 return;
1000 prompter.set_title (_("Rename Playlist"));
1001 prompter.set_prompt (_("New name for playlist:"));
1002 prompter.set_initial_text (pl->name());
1003 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
1004 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
1006 switch (prompter.run ()) {
1007 case Gtk::RESPONSE_ACCEPT:
1008 prompter.get_result (name);
1009 if (name.length()) {
1010 pl->set_name (name);
1012 break;
1014 default:
1015 break;
1019 std::string
1020 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1022 std::string ret (basename);
1024 std::string const group_string = "." + route_group()->name() + ".";
1026 // iterate through all playlists
1027 int maxnumber = 0;
1028 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1029 std::string tmp = (*i)->name();
1031 std::string::size_type idx = tmp.find(group_string);
1032 // find those which belong to this group
1033 if (idx != string::npos) {
1034 tmp = tmp.substr(idx + group_string.length());
1036 // and find the largest current number
1037 int x = atoi(tmp.c_str());
1038 if (x > maxnumber) {
1039 maxnumber = x;
1044 maxnumber++;
1046 char buf[32];
1047 snprintf (buf, sizeof(buf), "%d", maxnumber);
1049 ret = this->name() + "." + route_group()->name () + "." + buf;
1051 return ret;
1054 void
1055 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1057 string name;
1059 boost::shared_ptr<Track> tr = track ();
1060 if (!tr || tr->destructive()) {
1061 return;
1064 boost::shared_ptr<const Playlist> pl = tr->playlist();
1065 if (!pl) {
1066 return;
1069 name = pl->name();
1071 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1072 name = resolve_new_group_playlist_name(name, playlists_before_op);
1075 while (_session->playlists->by_name(name)) {
1076 name = Playlist::bump_name (name, *_session);
1079 // TODO: The prompter "new" button should be de-activated if the user
1080 // specifies a playlist name which already exists in the session.
1082 if (prompt) {
1084 ArdourPrompter prompter (true);
1086 prompter.set_title (_("New Copy Playlist"));
1087 prompter.set_prompt (_("Name for new playlist:"));
1088 prompter.set_initial_text (name);
1089 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1090 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1091 prompter.show_all ();
1093 switch (prompter.run ()) {
1094 case Gtk::RESPONSE_ACCEPT:
1095 prompter.get_result (name);
1096 break;
1098 default:
1099 return;
1103 if (name.length()) {
1104 tr->use_copy_playlist ();
1105 tr->playlist()->set_name (name);
1109 void
1110 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1112 string name;
1114 boost::shared_ptr<Track> tr = track ();
1115 if (!tr || tr->destructive()) {
1116 return;
1119 boost::shared_ptr<const Playlist> pl = tr->playlist();
1120 if (!pl) {
1121 return;
1124 name = pl->name();
1126 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1127 name = resolve_new_group_playlist_name(name,playlists_before_op);
1130 while (_session->playlists->by_name(name)) {
1131 name = Playlist::bump_name (name, *_session);
1135 if (prompt) {
1137 ArdourPrompter prompter (true);
1139 prompter.set_title (_("New Playlist"));
1140 prompter.set_prompt (_("Name for new playlist:"));
1141 prompter.set_initial_text (name);
1142 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1143 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1145 switch (prompter.run ()) {
1146 case Gtk::RESPONSE_ACCEPT:
1147 prompter.get_result (name);
1148 break;
1150 default:
1151 return;
1155 if (name.length()) {
1156 tr->use_new_playlist ();
1157 tr->playlist()->set_name (name);
1161 void
1162 RouteTimeAxisView::clear_playlist ()
1164 boost::shared_ptr<Track> tr = track ();
1165 if (!tr || tr->destructive()) {
1166 return;
1169 boost::shared_ptr<Playlist> pl = tr->playlist();
1170 if (!pl) {
1171 return;
1174 _editor.clear_playlist (pl);
1177 void
1178 RouteTimeAxisView::speed_changed ()
1180 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1183 void
1184 RouteTimeAxisView::update_diskstream_display ()
1186 if (!track()) {
1187 return;
1190 map_frozen ();
1193 void
1194 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1196 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1198 /* special case: select/deselect all tracks */
1199 if (_editor.get_selection().selected (this)) {
1200 _editor.get_selection().clear_tracks ();
1201 } else {
1202 _editor.select_all_tracks ();
1205 return;
1208 switch (ArdourKeyboard::selection_type (ev->state)) {
1209 case Selection::Toggle:
1210 _editor.get_selection().toggle (this);
1211 break;
1213 case Selection::Set:
1214 _editor.get_selection().set (this);
1215 break;
1217 case Selection::Extend:
1218 _editor.extend_selection_to_track (*this);
1219 break;
1221 case Selection::Add:
1222 _editor.get_selection().add (this);
1223 break;
1227 void
1228 RouteTimeAxisView::set_selected_points (PointSelection& points)
1230 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1231 (*i)->set_selected_points (points);
1235 void
1236 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1238 if (_view) {
1239 _view->set_selected_regionviews (regions);
1243 /** Add the selectable things that we have to a list.
1244 * @param results List to add things to.
1246 void
1247 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1249 double speed = 1.0;
1251 if (track() != 0) {
1252 speed = track()->speed();
1255 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1256 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1258 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1259 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1262 /* pick up visible automation tracks */
1264 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1265 if (!(*i)->hidden()) {
1266 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1271 void
1272 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1274 if (_view) {
1275 _view->get_inverted_selectables (sel, results);
1278 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1279 if (!(*i)->hidden()) {
1280 (*i)->get_inverted_selectables (sel, results);
1284 return;
1287 RouteGroup*
1288 RouteTimeAxisView::route_group () const
1290 return _route->route_group();
1293 string
1294 RouteTimeAxisView::name() const
1296 return _route->name();
1299 boost::shared_ptr<Playlist>
1300 RouteTimeAxisView::playlist () const
1302 boost::shared_ptr<Track> tr;
1304 if ((tr = track()) != 0) {
1305 return tr->playlist();
1306 } else {
1307 return boost::shared_ptr<Playlist> ();
1311 void
1312 RouteTimeAxisView::name_entry_changed ()
1314 string x;
1316 x = name_entry.get_text ();
1318 if (x == _route->name()) {
1319 return;
1322 strip_whitespace_edges(x);
1324 if (x.length() == 0) {
1325 name_entry.set_text (_route->name());
1326 return;
1329 if (!_session->route_name_unique (x)) {
1330 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1331 name_entry.set_text (_route->name());
1332 } else if (_session->route_name_internal (x)) {
1333 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1334 PROGRAM_NAME));
1335 name_entry.set_text (_route->name());
1336 } else {
1337 _route->set_name (x);
1341 boost::shared_ptr<Region>
1342 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1344 boost::shared_ptr<Playlist> pl = playlist ();
1346 if (pl) {
1347 return pl->find_next_region (pos, point, dir);
1350 return boost::shared_ptr<Region> ();
1353 framepos_t
1354 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1356 boost::shared_ptr<Playlist> pl = playlist ();
1358 if (pl) {
1359 return pl->find_next_region_boundary (pos, dir);
1362 return -1;
1365 void
1366 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1368 boost::shared_ptr<Playlist> what_we_got;
1369 boost::shared_ptr<Track> tr = track ();
1370 boost::shared_ptr<Playlist> playlist;
1372 if (tr == 0) {
1373 /* route is a bus, not a track */
1374 return;
1377 playlist = tr->playlist();
1379 TimeSelection time (selection.time);
1380 float const speed = tr->speed();
1381 if (speed != 1.0f) {
1382 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1383 (*i).start = session_frame_to_track_frame((*i).start, speed);
1384 (*i).end = session_frame_to_track_frame((*i).end, speed);
1388 playlist->clear_changes ();
1389 playlist->clear_owned_changes ();
1391 switch (op) {
1392 case Cut:
1393 if ((what_we_got = playlist->cut (time)) != 0) {
1394 _editor.get_cut_buffer().add (what_we_got);
1396 vector<Command*> cmds;
1397 playlist->rdiff (cmds);
1398 _session->add_commands (cmds);
1400 _session->add_command (new StatefulDiffCommand (playlist));
1402 break;
1403 case Copy:
1404 if ((what_we_got = playlist->copy (time)) != 0) {
1405 _editor.get_cut_buffer().add (what_we_got);
1407 break;
1409 case Clear:
1410 if ((what_we_got = playlist->cut (time)) != 0) {
1412 vector<Command*> cmds;
1413 playlist->rdiff (cmds);
1414 _session->add_commands (cmds);
1415 _session->add_command (new StatefulDiffCommand (playlist));
1416 what_we_got->release ();
1418 break;
1422 bool
1423 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1425 if (!is_track()) {
1426 return false;
1429 boost::shared_ptr<Playlist> pl = playlist ();
1430 PlaylistSelection::iterator p;
1432 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1434 if (p == selection.playlists.end()) {
1435 return false;
1438 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1440 if (track()->speed() != 1.0f) {
1441 pos = session_frame_to_track_frame (pos, track()->speed());
1442 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1445 pl->clear_changes ();
1446 pl->paste (*p, pos, times);
1447 _session->add_command (new StatefulDiffCommand (pl));
1449 return true;
1453 struct PlaylistSorter {
1454 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1455 return a->sort_id() < b->sort_id();
1459 void
1460 RouteTimeAxisView::build_playlist_menu ()
1462 using namespace Menu_Helpers;
1464 if (!is_track()) {
1465 return;
1468 delete playlist_action_menu;
1469 playlist_action_menu = new Menu;
1470 playlist_action_menu->set_name ("ArdourContextMenu");
1472 MenuList& playlist_items = playlist_action_menu->items();
1473 playlist_action_menu->set_name ("ArdourContextMenu");
1474 playlist_items.clear();
1476 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1477 boost::shared_ptr<Track> tr = track();
1478 RadioMenuItem::Group playlist_group;
1480 _session->playlists->get (playlists);
1482 /* find the playlists for this diskstream */
1483 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1484 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1485 playlists_tr.push_back(*i);
1489 /* sort the playlists */
1490 PlaylistSorter cmp;
1491 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1493 /* add the playlists to the menu */
1494 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1495 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1496 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1497 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1499 if (tr->playlist()->id() == (*i)->id()) {
1500 item->set_active();
1505 playlist_items.push_back (SeparatorElem());
1506 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1507 playlist_items.push_back (SeparatorElem());
1509 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1510 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1511 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1513 } else {
1514 // Use a label which tells the user what is happening
1515 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1516 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1520 playlist_items.push_back (SeparatorElem());
1521 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1522 playlist_items.push_back (SeparatorElem());
1524 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1527 void
1528 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1530 assert (is_track());
1532 // exit if we were triggered by deactivating the old playlist
1533 if (!item->get_active()) {
1534 return;
1537 boost::shared_ptr<Playlist> pl (wpl.lock());
1539 if (!pl) {
1540 return;
1543 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1545 if (apl) {
1546 if (track()->playlist() == apl) {
1547 // exit when use_playlist is called by the creation of the playlist menu
1548 // or the playlist choice is unchanged
1549 return;
1551 track()->use_playlist (apl);
1553 RouteGroup* rg = route_group();
1555 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1556 std::string group_string = "." + rg->name() + ".";
1558 std::string take_name = apl->name();
1559 std::string::size_type idx = take_name.find(group_string);
1561 if (idx == std::string::npos)
1562 return;
1564 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1566 boost::shared_ptr<RouteList> rl (rg->route_list());
1568 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1569 if ( (*i) == this->route()) {
1570 continue;
1573 std::string playlist_name = (*i)->name()+group_string+take_name;
1575 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1576 if (!track) {
1577 continue;
1580 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1581 if (!ipl) {
1582 // No playlist for this track for this take yet, make it
1583 track->use_new_playlist();
1584 track->playlist()->set_name(playlist_name);
1585 } else {
1586 track->use_playlist(ipl);
1593 void
1594 RouteTimeAxisView::show_playlist_selector ()
1596 _editor.playlist_selector().show_for (this);
1599 void
1600 RouteTimeAxisView::map_frozen ()
1602 if (!is_track()) {
1603 return;
1606 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1608 switch (track()->freeze_state()) {
1609 case Track::Frozen:
1610 playlist_button.set_sensitive (false);
1611 rec_enable_button->set_sensitive (false);
1612 break;
1613 default:
1614 playlist_button.set_sensitive (true);
1615 rec_enable_button->set_sensitive (true);
1616 break;
1620 void
1621 RouteTimeAxisView::color_handler ()
1623 //case cTimeStretchOutline:
1624 if (timestretch_rect) {
1625 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1627 //case cTimeStretchFill:
1628 if (timestretch_rect) {
1629 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1632 reset_meter();
1635 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1636 * Will add track if necessary.
1638 void
1639 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1641 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1642 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1644 if (!track) {
1645 /* it doesn't exist yet, so we don't care about the button state: just add it */
1646 create_automation_child (param, true);
1647 } else {
1648 assert (menu);
1649 bool yn = menu->get_active();
1650 if (track->set_visibility (menu->get_active()) && yn) {
1652 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1653 will have done that for us.
1656 if (!no_redraw) {
1657 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1663 void
1664 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1666 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1668 if (!track) {
1669 return;
1672 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1674 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1675 track->get_state_node()->add_property (X_("shown"), X_("no"));
1677 if (menu && !_hidden) {
1678 ignore_toggle = true;
1679 menu->set_active (false);
1680 ignore_toggle = false;
1683 if (_route && !no_redraw) {
1684 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1689 void
1690 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1692 if (apply_to_selection) {
1693 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1694 } else {
1695 no_redraw = true;
1697 /* Show our automation */
1699 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1700 i->second->set_marked_for_display (true);
1701 i->second->canvas_display()->show();
1702 i->second->get_state_node()->add_property ("shown", X_("yes"));
1704 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1706 if (menu) {
1707 menu->set_active(true);
1712 /* Show processor automation */
1714 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1715 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1716 if ((*ii)->view == 0) {
1717 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1720 (*ii)->menu_item->set_active (true);
1724 no_redraw = false;
1726 /* Redraw */
1728 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1732 void
1733 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1735 if (apply_to_selection) {
1736 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1737 } else {
1738 no_redraw = true;
1740 /* Show our automation */
1742 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1743 if (i->second->has_automation()) {
1744 i->second->set_marked_for_display (true);
1745 i->second->canvas_display()->show();
1746 i->second->get_state_node()->add_property ("shown", X_("yes"));
1748 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1749 if (menu) {
1750 menu->set_active(true);
1756 /* Show processor automation */
1758 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1759 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1760 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1761 (*ii)->menu_item->set_active (true);
1766 no_redraw = false;
1768 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1772 void
1773 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1775 if (apply_to_selection) {
1776 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1777 } else {
1778 no_redraw = true;
1780 /* Hide our automation */
1782 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1783 i->second->set_marked_for_display (false);
1784 i->second->hide ();
1785 i->second->get_state_node()->add_property ("shown", X_("no"));
1787 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1789 if (menu) {
1790 menu->set_active (false);
1794 /* Hide processor automation */
1796 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1797 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1798 (*ii)->menu_item->set_active (false);
1802 no_redraw = false;
1803 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1808 void
1809 RouteTimeAxisView::region_view_added (RegionView* rv)
1811 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1812 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1813 boost::shared_ptr<AutomationTimeAxisView> atv;
1815 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1816 atv->add_ghost(rv);
1820 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1821 (*i)->add_ghost(rv);
1825 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1827 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1828 delete *i;
1833 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1835 parent.remove_processor_automation_node (this);
1838 void
1839 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1841 if (pan->view) {
1842 remove_child (pan->view);
1846 RouteTimeAxisView::ProcessorAutomationNode*
1847 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1849 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1851 if ((*i)->processor == processor) {
1853 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1854 if ((*ii)->what == what) {
1855 return *ii;
1861 return 0;
1864 static string
1865 legalize_for_xml_node (string str)
1867 string::size_type pos;
1868 string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1869 string legal;
1871 legal = str;
1872 pos = 0;
1874 while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1875 legal.replace (pos, 1, "_");
1876 pos += 1;
1879 return legal;
1883 void
1884 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1886 string name;
1887 ProcessorAutomationNode* pan;
1889 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1890 /* session state may never have been saved with new plugin */
1891 error << _("programming error: ")
1892 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1893 processor->name(), what.type(), (int) what.channel(), what.id() )
1894 << endmsg;
1895 /*NOTREACHED*/
1896 return;
1899 if (pan->view) {
1900 return;
1903 name = processor->describe_parameter (what);
1905 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1907 /* FIXME: ew */
1909 char state_name[256];
1910 snprintf (state_name, sizeof (state_name), "%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1912 boost::shared_ptr<AutomationControl> control
1913 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1915 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1916 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1917 _editor, *this, false, parent_canvas, name, state_name));
1919 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1921 if (!pan->view->marked_for_display()) {
1922 pan->view->hide ();
1923 } else {
1924 pan->menu_item->set_active (true);
1927 add_child (pan->view);
1929 if (_view) {
1930 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1933 processor->mark_automation_visible (what, true);
1936 void
1937 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1939 if (!_hidden) {
1940 pan->menu_item->set_active (false);
1943 i->mark_automation_visible (pan->what, false);
1945 if (!no_redraw) {
1946 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1950 void
1951 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1953 boost::shared_ptr<Processor> processor (p.lock ());
1954 if (!processor) {
1955 return;
1958 set<Evoral::Parameter> s;
1959 boost::shared_ptr<AutomationLine> al;
1961 processor->what_has_visible_data (s);
1963 for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1965 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1966 al->queue_reset ();
1967 } else {
1968 add_processor_automation_curve (processor, (*i));
1973 void
1974 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1976 using namespace Menu_Helpers;
1978 XMLProperty* prop;
1979 XMLNode* node;
1981 add_child (track);
1983 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1985 bool hideit = (!show);
1987 if ((node = track->get_state_node()) != 0) {
1988 if ((prop = node->property ("shown")) != 0) {
1989 if (string_is_affirmative (prop->value())) {
1990 hideit = false;
1995 _automation_tracks[param] = track;
1997 track->set_visibility (!hideit);
1999 if (!no_redraw) {
2000 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2003 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2004 /* MIDI-related parameters are always in the menu, there's no
2005 reason to rebuild the menu just because we added a automation
2006 lane for one of them. But if we add a non-MIDI automation
2007 lane, then we need to invalidate the display menu.
2009 delete display_menu;
2010 display_menu = 0;
2014 void
2015 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2017 boost::shared_ptr<Processor> processor (p.lock ());
2019 if (!processor || !processor->display_to_user ()) {
2020 return;
2023 using namespace Menu_Helpers;
2024 ProcessorAutomationInfo *rai;
2025 list<ProcessorAutomationInfo*>::iterator x;
2027 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2028 std::set<Evoral::Parameter> has_visible_automation;
2030 processor->what_has_visible_data(has_visible_automation);
2032 if (automatable.empty()) {
2033 return;
2036 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2037 if ((*x)->processor == processor) {
2038 break;
2042 if (x == processor_automation.end()) {
2044 rai = new ProcessorAutomationInfo (processor);
2045 processor_automation.push_back (rai);
2047 } else {
2049 rai = *x;
2053 /* any older menu was deleted at the top of processors_changed()
2054 when we cleared the subplugin menu.
2057 rai->menu = manage (new Menu);
2058 MenuList& items = rai->menu->items();
2059 rai->menu->set_name ("ArdourContextMenu");
2061 items.clear ();
2063 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2065 ProcessorAutomationNode* pan;
2066 CheckMenuItem* mitem;
2068 string name = processor->describe_parameter (*i);
2070 items.push_back (CheckMenuElem (name));
2071 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2073 _subplugin_menu_map[*i] = mitem;
2075 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2076 mitem->set_active(true);
2079 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2081 /* new item */
2083 pan = new ProcessorAutomationNode (*i, mitem, *this);
2085 rai->lines.push_back (pan);
2087 } else {
2089 pan->menu_item = mitem;
2093 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2096 /* add the menu for this processor, because the subplugin
2097 menu is always cleared at the top of processors_changed().
2098 this is the result of some poor design in gtkmm and/or
2099 GTK+.
2102 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2103 rai->valid = true;
2106 void
2107 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2108 RouteTimeAxisView::ProcessorAutomationNode* pan)
2110 bool showit = pan->menu_item->get_active();
2111 bool redraw = false;
2113 if (pan->view == 0 && showit) {
2114 add_processor_automation_curve (rai->processor, pan->what);
2115 redraw = true;
2118 if (pan->view && showit != pan->view->marked_for_display()) {
2120 if (showit) {
2121 pan->view->set_marked_for_display (true);
2122 pan->view->canvas_display()->show();
2123 pan->view->canvas_background()->show();
2124 } else {
2125 rai->processor->mark_automation_visible (pan->what, true);
2126 pan->view->set_marked_for_display (false);
2127 pan->view->hide ();
2130 redraw = true;
2134 if (redraw && !no_redraw) {
2135 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2140 void
2141 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2143 if (c.type == RouteProcessorChange::MeterPointChange) {
2144 /* nothing to do if only the meter point has changed */
2145 return;
2148 using namespace Menu_Helpers;
2150 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2151 (*i)->valid = false;
2154 _subplugin_menu_map.clear ();
2155 subplugin_menu.items().clear ();
2157 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2158 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2160 bool deleted_processor_automation = false;
2162 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2164 list<ProcessorAutomationInfo*>::iterator tmp;
2166 tmp = i;
2167 ++tmp;
2169 if (!(*i)->valid) {
2171 delete *i;
2172 processor_automation.erase (i);
2173 deleted_processor_automation = true;
2177 i = tmp;
2180 if (deleted_processor_automation && !no_redraw) {
2181 _route->gui_changed ("track_height", this);
2185 boost::shared_ptr<AutomationLine>
2186 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2188 ProcessorAutomationNode* pan;
2190 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2191 if (pan->view) {
2192 pan->view->line();
2196 return boost::shared_ptr<AutomationLine>();
2199 void
2200 RouteTimeAxisView::reset_processor_automation_curves ()
2202 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2203 (*i)->reset();
2207 void
2208 RouteTimeAxisView::update_rec_display ()
2210 RouteUI::update_rec_display ();
2211 name_entry.set_sensitive (!_route->record_enabled());
2214 void
2215 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2217 if (apply_to_selection) {
2218 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2219 } else {
2221 if (_view) {
2222 _view->set_layer_display (d);
2225 ensure_xml_node ();
2226 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2230 LayerDisplay
2231 RouteTimeAxisView::layer_display () const
2233 if (_view) {
2234 return _view->layer_display ();
2237 /* we don't know, since we don't have a _view, so just return something */
2238 return Overlaid;
2243 boost::shared_ptr<AutomationTimeAxisView>
2244 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2246 AutomationTracks::iterator i = _automation_tracks.find(param);
2247 if (i != _automation_tracks.end()) {
2248 return i->second;
2249 } else {
2250 return boost::shared_ptr<AutomationTimeAxisView>();
2254 void
2255 RouteTimeAxisView::fast_update ()
2257 gm.get_level_meter().update_meters ();
2260 void
2261 RouteTimeAxisView::hide_meter ()
2263 clear_meter ();
2264 gm.get_level_meter().hide_meters ();
2267 void
2268 RouteTimeAxisView::show_meter ()
2270 reset_meter ();
2273 void
2274 RouteTimeAxisView::reset_meter ()
2276 if (Config->get_show_track_meters()) {
2277 gm.get_level_meter().setup_meters (height-5);
2278 } else {
2279 hide_meter ();
2283 void
2284 RouteTimeAxisView::clear_meter ()
2286 gm.get_level_meter().clear_meters ();
2289 void
2290 RouteTimeAxisView::meter_changed ()
2292 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2293 reset_meter();
2296 void
2297 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2299 reset_meter ();
2302 void
2303 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2305 using namespace Menu_Helpers;
2307 if (!_underlay_streams.empty()) {
2308 MenuList& parent_items = parent_menu->items();
2309 Menu* gs_menu = manage (new Menu);
2310 gs_menu->set_name ("ArdourContextMenu");
2311 MenuList& gs_items = gs_menu->items();
2313 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2315 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2316 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2317 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2322 bool
2323 RouteTimeAxisView::set_underlay_state()
2325 if (!underlay_xml_node) {
2326 return false;
2329 XMLNodeList nlist = underlay_xml_node->children();
2330 XMLNodeConstIterator niter;
2331 XMLNode *child_node;
2333 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2334 child_node = *niter;
2336 if (child_node->name() != "Underlay") {
2337 continue;
2340 XMLProperty* prop = child_node->property ("id");
2341 if (prop) {
2342 PBD::ID id (prop->value());
2344 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2346 if (v) {
2347 add_underlay(v->view(), false);
2352 return false;
2355 void
2356 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2358 if (!v) {
2359 return;
2362 RouteTimeAxisView& other = v->trackview();
2364 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2365 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2366 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2367 /*NOTREACHED*/
2370 _underlay_streams.push_back(v);
2371 other._underlay_mirrors.push_back(this);
2373 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2375 if (update_xml) {
2376 if (!underlay_xml_node) {
2377 ensure_xml_node();
2378 underlay_xml_node = xml_node->add_child("Underlays");
2381 XMLNode* node = underlay_xml_node->add_child("Underlay");
2382 XMLProperty* prop = node->add_property("id");
2383 prop->set_value(v->trackview().route()->id().to_s());
2388 void
2389 RouteTimeAxisView::remove_underlay (StreamView* v)
2391 if (!v) {
2392 return;
2395 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2396 RouteTimeAxisView& other = v->trackview();
2398 if (it != _underlay_streams.end()) {
2399 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2401 if (gm == other._underlay_mirrors.end()) {
2402 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2403 /*NOTREACHED*/
2406 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2408 _underlay_streams.erase(it);
2409 other._underlay_mirrors.erase(gm);
2411 if (underlay_xml_node) {
2412 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2417 void
2418 RouteTimeAxisView::set_button_names ()
2420 rec_enable_button_label.set_text (_("r"));
2422 if (_route && _route->solo_safe()) {
2423 solo_button_label.set_text (X_("!"));
2424 } else {
2425 if (Config->get_solo_control_is_listen_control()) {
2426 switch (Config->get_listen_position()) {
2427 case AfterFaderListen:
2428 solo_button_label.set_text (_("A"));
2429 break;
2430 case PreFaderListen:
2431 solo_button_label.set_text (_("P"));
2432 break;
2434 } else {
2435 solo_button_label.set_text (_("s"));
2438 mute_button_label.set_text (_("m"));
2441 Gtk::CheckMenuItem*
2442 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2444 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2445 if (i != _main_automation_menu_map.end()) {
2446 return i->second;
2449 i = _subplugin_menu_map.find (param);
2450 if (i != _subplugin_menu_map.end()) {
2451 return i->second;
2454 return 0;
2457 void
2458 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2460 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2461 if (!c) {
2462 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2463 return;
2466 gain_track.reset (new AutomationTimeAxisView (_session,
2467 _route, _route->amp(), c, param,
2468 _editor,
2469 *this,
2470 false,
2471 parent_canvas,
2472 _route->amp()->describe_parameter(param)));
2474 if (_view) {
2475 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2478 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2481 static
2482 void add_region_to_list (RegionView* rv, Playlist::RegionList* l, uint32_t* max_level)
2484 l->push_back (rv->region());
2485 *max_level = max (*max_level, rv->region()->max_source_level());
2488 void
2489 RouteTimeAxisView::combine_regions ()
2491 assert (is_track());
2493 if (!_view) {
2494 return;
2497 Playlist::RegionList selected_regions;
2498 boost::shared_ptr<Playlist> playlist = track()->playlist();
2499 uint32_t max_level = 0;
2501 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions, &max_level));
2503 string name = string_compose (_("%1 compound-%2 (%3)"), playlist->name(), playlist->combine_ops()+1, max_level+1);
2505 playlist->clear_changes ();
2506 playlist->join (selected_regions, name);
2507 _session->add_command (new StatefulDiffCommand (playlist));