Zoom session when the mouse pointer is moved up and down during a playhead drag.
[ardour2.git] / gtk2_ardour / route_time_axis.cc
blobf1e00978e98b39b2940d24116069ec835ce7e4db
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/region_factory.h"
57 #include "ardour/route_group.h"
58 #include "ardour/session.h"
59 #include "ardour/session_playlist.h"
60 #include "ardour/debug.h"
61 #include "ardour/utils.h"
62 #include "evoral/Parameter.hpp"
64 #include "ardour_ui.h"
65 #include "debug.h"
66 #include "global_signals.h"
67 #include "route_time_axis.h"
68 #include "automation_time_axis.h"
69 #include "canvas_impl.h"
70 #include "crossfade_view.h"
71 #include "enums.h"
72 #include "gui_thread.h"
73 #include "keyboard.h"
74 #include "playlist_selector.h"
75 #include "point_selection.h"
76 #include "prompter.h"
77 #include "public_editor.h"
78 #include "region_view.h"
79 #include "rgb_macros.h"
80 #include "selection.h"
81 #include "simplerect.h"
82 #include "streamview.h"
83 #include "utils.h"
84 #include "route_group_menu.h"
86 #include "ardour/track.h"
88 #include "i18n.h"
90 using namespace ARDOUR;
91 using namespace PBD;
92 using namespace Gtkmm2ext;
93 using namespace Gtk;
94 using namespace Editing;
95 using namespace std;
97 Glib::RefPtr<Gdk::Pixbuf> RouteTimeAxisView::slider;
99 void
100 RouteTimeAxisView::setup_slider_pix ()
102 if ((slider = ::get_icon ("fader_belt_h")) == 0) {
103 throw failed_constructor ();
107 RouteTimeAxisView::RouteTimeAxisView (PublicEditor& ed, Session* sess, boost::shared_ptr<Route> rt, Canvas& canvas)
108 : AxisView(sess)
109 , RouteUI(rt, sess)
110 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
111 , parent_canvas (canvas)
112 , button_table (3, 3)
113 , route_group_button (_("g"))
114 , playlist_button (_("p"))
115 , automation_button (_("a"))
116 , gm (sess, slider, true, 115)
118 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
119 gm.get_level_meter().set_no_show_all();
120 gm.get_level_meter().setup_meters(50);
122 _has_state = true;
123 playlist_action_menu = 0;
124 automation_action_menu = 0;
125 plugins_submenu_item = 0;
126 mode_menu = 0;
127 _view = 0;
129 if (!_route->is_hidden()) {
130 _marked_for_display = true;
133 mute_changed (0);
134 update_solo_display ();
136 timestretch_rect = 0;
137 no_redraw = false;
139 ignore_toggle = false;
141 route_group_button.set_name ("TrackGroupButton");
142 playlist_button.set_name ("TrackPlaylistButton");
143 automation_button.set_name ("TrackAutomationButton");
145 route_group_button.unset_flags (Gtk::CAN_FOCUS);
146 playlist_button.unset_flags (Gtk::CAN_FOCUS);
147 automation_button.unset_flags (Gtk::CAN_FOCUS);
149 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
150 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
151 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
153 if (is_track()) {
155 /* use icon */
157 rec_enable_button->remove ();
159 switch (track()->mode()) {
160 case ARDOUR::Normal:
161 case ARDOUR::NonLayered:
162 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
163 break;
164 case ARDOUR::Destructive:
165 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
166 break;
168 rec_enable_button->show_all ();
170 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
172 if (is_midi_track()) {
173 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
174 } else {
175 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
178 rec_enable_button->set_sensitive (_session->writable());
181 controls_hbox.pack_start(gm.get_level_meter(), false, false);
182 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
183 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
184 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
186 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
188 if (!_route->is_master()) {
189 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
192 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
193 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
195 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
196 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
197 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
198 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
199 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
201 label_view ();
203 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
205 if (is_track() && track()->mode() == ARDOUR::Normal) {
206 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
209 _y_position = -1;
211 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
212 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
214 if (is_track()) {
216 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
217 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
219 /* pick up the correct freeze state */
220 map_frozen ();
224 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
225 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
226 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
228 PropertyList* plist = new PropertyList();
230 plist->add (ARDOUR::Properties::edit, true);
231 plist->add (ARDOUR::Properties::mute, true);
232 plist->add (ARDOUR::Properties::solo, true);
234 route_group_menu = new RouteGroupMenu (_session, plist);
236 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
237 gm.get_gain_slider().set_name ("TrackGainFader");
239 show_name_entry ();
240 hide_name_label ();
243 RouteTimeAxisView::~RouteTimeAxisView ()
245 CatchDeletion (this);
247 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
248 delete *i;
251 delete playlist_action_menu;
252 playlist_action_menu = 0;
254 delete _view;
255 _view = 0;
257 _automation_tracks.clear ();
259 delete route_group_menu;
262 void
263 RouteTimeAxisView::post_construct ()
265 /* map current state of the route */
267 update_diskstream_display ();
269 _subplugin_menu_map.clear ();
270 subplugin_menu.items().clear ();
271 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
272 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
273 reset_processor_automation_curves ();
276 gint
277 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
279 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
280 if (_route->route_group()) {
281 _route->route_group()->remove (_route);
283 return false;
286 WeakRouteList r;
287 r.push_back (route ());
289 route_group_menu->build (r);
290 route_group_menu->menu()->popup (ev->button, ev->time);
292 return false;
295 void
296 RouteTimeAxisView::playlist_changed ()
298 label_view ();
301 void
302 RouteTimeAxisView::label_view ()
304 string x = _route->name();
306 if (x != name_entry.get_text()) {
307 name_entry.set_text (x);
310 if (x != name_label.get_text()) {
311 name_label.set_text (x);
314 ARDOUR_UI::instance()->set_tip (name_entry, x);
317 void
318 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
320 if (what_changed.contains (ARDOUR::Properties::name)) {
321 label_view ();
325 void
326 RouteTimeAxisView::take_name_changed (void *src)
328 if (src != this) {
329 label_view ();
333 void
334 RouteTimeAxisView::playlist_click ()
336 build_playlist_menu ();
337 conditionally_add_to_selection ();
338 playlist_action_menu->popup (1, gtk_get_current_event_time());
341 void
342 RouteTimeAxisView::automation_click ()
344 conditionally_add_to_selection ();
345 build_automation_action_menu (false);
346 automation_action_menu->popup (1, gtk_get_current_event_time());
350 RouteTimeAxisView::set_state (const XMLNode& node, int version)
352 TimeAxisView::set_state (node, version);
354 XMLNodeList kids = node.children();
355 XMLNodeConstIterator iter;
356 const XMLProperty* prop;
358 if (_view && (prop = node.property ("layer-display"))) {
359 set_layer_display (LayerDisplay (string_2_enum (prop->value(), _view->layer_display ())));
362 for (iter = kids.begin(); iter != kids.end(); ++iter) {
363 if ((*iter)->name() == AutomationTimeAxisView::state_node_name) {
364 if ((prop = (*iter)->property ("automation-id")) != 0) {
366 Evoral::Parameter param = ARDOUR::EventTypeMap::instance().new_parameter(prop->value());
367 bool show = ((prop = (*iter)->property ("shown")) != 0) && string_is_affirmative (prop->value());
368 create_automation_child(param, show);
369 } else {
370 warning << "Automation child has no ID" << endmsg;
375 return 0;
378 void
379 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
381 using namespace Menu_Helpers;
383 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
384 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
387 detach_menu (subplugin_menu);
389 _main_automation_menu_map.clear ();
390 delete automation_action_menu;
391 automation_action_menu = new Menu;
393 MenuList& items = automation_action_menu->items();
395 automation_action_menu->set_name ("ArdourContextMenu");
397 items.push_back (MenuElem (_("Show All Automation"),
398 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
400 items.push_back (MenuElem (_("Show Existing Automation"),
401 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
403 items.push_back (MenuElem (_("Hide All Automation"),
404 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
406 items.push_back (SeparatorElem ());
408 /* Attach the plugin submenu. It may have previously been used elsewhere,
409 so it was detached above */
411 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
412 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
415 void
416 RouteTimeAxisView::build_display_menu ()
418 using namespace Menu_Helpers;
420 /* prepare it */
422 TimeAxisView::build_display_menu ();
424 /* now fill it with our stuff */
426 MenuList& items = display_menu->items();
427 display_menu->set_name ("ArdourContextMenu");
429 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
431 if (_size_menu) {
432 detach_menu (*_size_menu);
434 build_size_menu ();
435 items.push_back (MenuElem (_("Height"), *_size_menu));
437 items.push_back (SeparatorElem());
439 if (!Profile->get_sae()) {
440 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
441 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
442 items.push_back (SeparatorElem());
445 // Hook for derived classes to add type specific stuff
446 append_extra_display_menu_items ();
448 if (is_track()) {
450 Menu* layers_menu = manage (new Menu);
451 MenuList &layers_items = layers_menu->items();
452 layers_menu->set_name("ArdourContextMenu");
454 RadioMenuItem::Group layers_group;
456 /* Find out how many overlaid/stacked tracks we have in the selection */
458 int overlaid = 0;
459 int stacked = 0;
460 TrackSelection const & s = _editor.get_selection().tracks;
461 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
462 StreamView* v = (*i)->view ();
463 if (!v) {
464 continue;
467 switch (v->layer_display ()) {
468 case Overlaid:
469 ++overlaid;
470 break;
471 case Stacked:
472 ++stacked;
473 break;
477 /* We're not connecting to signal_toggled() here; in the case where these two items are
478 set to be in the `inconsistent' state, it seems that one or other will end up active
479 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
480 select the active one, no toggled signal is emitted so nothing happens.
483 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
484 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
485 i->set_active (overlaid != 0 && stacked == 0);
486 i->set_inconsistent (overlaid != 0 && stacked != 0);
487 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
489 layers_items.push_back (
490 RadioMenuElem (layers_group, _("Stacked"),
491 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
494 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
495 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
496 i->set_active (overlaid == 0 && stacked != 0);
497 i->set_inconsistent (overlaid != 0 && stacked != 0);
499 items.push_back (MenuElem (_("Layers"), *layers_menu));
501 if (!Profile->get_sae()) {
503 Menu* alignment_menu = manage (new Menu);
504 MenuList& alignment_items = alignment_menu->items();
505 alignment_menu->set_name ("ArdourContextMenu");
507 RadioMenuItem::Group align_group;
509 /* Same verbose hacks as for the layering options above */
511 int existing = 0;
512 int capture = 0;
513 int automatic = 0;
514 int styles = 0;
515 boost::shared_ptr<Track> first_track;
517 TrackSelection const & s = _editor.get_selection().tracks;
518 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
519 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
520 if (!r || !r->is_track ()) {
521 continue;
524 if (!first_track) {
525 first_track = r->track();
528 switch (r->track()->alignment_choice()) {
529 case Automatic:
530 ++automatic;
531 styles |= 0x1;
532 switch (r->track()->alignment_style()) {
533 case ExistingMaterial:
534 ++existing;
535 break;
536 case CaptureTime:
537 ++capture;
538 break;
540 break;
541 case UseExistingMaterial:
542 ++existing;
543 styles |= 0x2;
544 break;
545 case UseCaptureTime:
546 ++capture;
547 styles |= 0x4;
548 break;
552 bool inconsistent;
553 switch (styles) {
554 case 1:
555 case 2:
556 case 4:
557 inconsistent = false;
558 break;
559 default:
560 inconsistent = true;
561 break;
564 RadioMenuItem* i;
566 if (!inconsistent && first_track) {
568 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
569 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
570 i->set_active (automatic != 0 && existing == 0 && capture == 0);
571 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
573 switch (first_track->alignment_choice()) {
574 case Automatic:
575 switch (first_track->alignment_style()) {
576 case ExistingMaterial:
577 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
578 break;
579 case CaptureTime:
580 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
581 break;
583 break;
584 default:
585 break;
588 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
589 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
590 i->set_active (existing != 0 && capture == 0 && automatic == 0);
591 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
593 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
594 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
595 i->set_active (existing == 0 && capture != 0 && automatic == 0);
596 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
598 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
600 } else {
601 /* show nothing */
604 Menu* mode_menu = manage (new Menu);
605 MenuList& mode_items = mode_menu->items ();
606 mode_menu->set_name ("ArdourContextMenu");
608 RadioMenuItem::Group mode_group;
610 int normal = 0;
611 int tape = 0;
612 int non_layered = 0;
614 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
615 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
616 if (!r || !r->is_track ()) {
617 continue;
620 switch (r->track()->mode()) {
621 case Normal:
622 ++normal;
623 break;
624 case Destructive:
625 ++tape;
626 break;
627 case NonLayered:
628 ++non_layered;
629 break;
633 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
634 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
635 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
636 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
637 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
639 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
640 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
641 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
642 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
643 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
645 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
646 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
647 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
648 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
649 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
651 items.push_back (MenuElem (_("Mode"), *mode_menu));
654 color_mode_menu = build_color_mode_menu();
655 if (color_mode_menu) {
656 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
659 items.push_back (SeparatorElem());
661 build_playlist_menu ();
662 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
663 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
665 route_group_menu->detach ();
667 WeakRouteList r;
668 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
669 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
670 if (rtv) {
671 r.push_back (rtv->route ());
675 if (r.empty ()) {
676 r.push_back (route ());
679 route_group_menu->build (r);
680 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
682 build_automation_action_menu (true);
683 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
685 items.push_back (SeparatorElem());
688 int active = 0;
689 int inactive = 0;
690 TrackSelection const & s = _editor.get_selection().tracks;
691 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
692 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
693 if (!r) {
694 continue;
697 if (r->route()->active()) {
698 ++active;
699 } else {
700 ++inactive;
704 items.push_back (CheckMenuElem (_("Active")));
705 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
706 bool click_sets_active = true;
707 if (active > 0 && inactive == 0) {
708 i->set_active (true);
709 click_sets_active = false;
710 } else if (active > 0 && inactive > 0) {
711 i->set_inconsistent (true);
713 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
715 items.push_back (SeparatorElem());
716 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
717 if (!Profile->get_sae()) {
718 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
719 } else {
720 items.push_front (SeparatorElem());
721 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
725 void
726 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
728 if (apply_to_selection) {
729 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
730 } else {
732 bool needs_bounce;
734 if (!track()->can_use_mode (mode, needs_bounce)) {
736 if (!needs_bounce) {
737 /* cannot be done */
738 return;
739 } else {
740 cerr << "would bounce this one\n";
741 return;
745 track()->set_mode (mode);
747 rec_enable_button->remove ();
749 switch (mode) {
750 case ARDOUR::NonLayered:
751 case ARDOUR::Normal:
752 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
753 break;
754 case ARDOUR::Destructive:
755 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
756 break;
759 rec_enable_button->show_all ();
763 void
764 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
766 double x1;
767 double x2;
768 double y2;
770 TimeAxisView::show_timestretch (start, end);
772 hide_timestretch ();
774 #if 0
775 if (ts.empty()) {
776 return;
780 /* check that the time selection was made in our route, or our route group.
781 remember that route_group() == 0 implies the route is *not* in a edit group.
784 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
785 /* this doesn't apply to us */
786 return;
789 /* ignore it if our edit group is not active */
791 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
792 return;
794 #endif
796 if (timestretch_rect == 0) {
797 timestretch_rect = new SimpleRect (*canvas_display ());
798 timestretch_rect->property_x1() = 0.0;
799 timestretch_rect->property_y1() = 0.0;
800 timestretch_rect->property_x2() = 0.0;
801 timestretch_rect->property_y2() = 0.0;
802 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
803 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
806 timestretch_rect->show ();
807 timestretch_rect->raise_to_top ();
809 x1 = start / _editor.get_current_zoom();
810 x2 = (end - 1) / _editor.get_current_zoom();
811 y2 = current_height() - 2;
813 timestretch_rect->property_x1() = x1;
814 timestretch_rect->property_y1() = 1.0;
815 timestretch_rect->property_x2() = x2;
816 timestretch_rect->property_y2() = y2;
819 void
820 RouteTimeAxisView::hide_timestretch ()
822 TimeAxisView::hide_timestretch ();
824 if (timestretch_rect) {
825 timestretch_rect->hide ();
829 void
830 RouteTimeAxisView::show_selection (TimeSelection& ts)
833 #if 0
834 /* ignore it if our edit group is not active or if the selection was started
835 in some other track or route group (remember that route_group() == 0 means
836 that the track is not in an route group).
839 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
840 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
841 hide_selection ();
842 return;
844 #endif
846 TimeAxisView::show_selection (ts);
849 void
850 RouteTimeAxisView::set_height (uint32_t h)
852 int gmlen = h - 5;
853 bool height_changed = (height == 0) || (h != height);
854 gm.get_level_meter().setup_meters (gmlen);
856 TimeAxisView::set_height (h);
858 ensure_xml_node ();
860 if (_view) {
861 _view->set_height ((double) current_height());
864 char buf[32];
865 snprintf (buf, sizeof (buf), "%u", height);
866 xml_node->add_property ("height", buf);
868 if (height >= preset_height (HeightNormal)) {
870 _controls_padding_table.set_row_spacings (2);
872 reset_meter();
874 gm.get_gain_slider().show();
875 mute_button->show();
876 if (!_route || _route->is_monitor()) {
877 solo_button->hide();
878 } else {
879 solo_button->show();
881 if (rec_enable_button)
882 rec_enable_button->show();
884 route_group_button.show();
885 automation_button.show();
887 if (is_track() && track()->mode() == ARDOUR::Normal) {
888 playlist_button.show();
891 } else if (height >= preset_height (HeightSmaller)) {
893 _controls_padding_table.set_row_spacings (2);
895 reset_meter();
897 gm.get_gain_slider().hide();
898 mute_button->show();
899 if (!_route || _route->is_monitor()) {
900 solo_button->hide();
901 } else {
902 solo_button->show();
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 ();
914 } else {
916 _controls_padding_table.set_row_spacings (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 */
926 void
927 RouteTimeAxisView::set_color (Gdk::Color const & c)
929 RouteUI::set_color (c);
931 if (_view) {
932 _view->apply_color (_color, StreamView::RegionColor);
936 void
937 RouteTimeAxisView::reset_samples_per_unit ()
939 set_samples_per_unit (_editor.get_current_zoom());
942 void
943 RouteTimeAxisView::horizontal_position_changed ()
945 if (_view) {
946 _view->horizontal_position_changed ();
950 void
951 RouteTimeAxisView::set_samples_per_unit (double spu)
953 double speed = 1.0;
955 if (track()) {
956 speed = track()->speed();
959 if (_view) {
960 _view->set_samples_per_unit (spu * speed);
963 TimeAxisView::set_samples_per_unit (spu * speed);
966 void
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.
973 return;
976 if (apply_to_selection) {
977 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
978 } else {
979 if (track ()) {
980 track()->set_align_choice (choice);
985 void
986 RouteTimeAxisView::rename_current_playlist ()
988 ArdourPrompter prompter (true);
989 string name;
991 boost::shared_ptr<Track> tr = track();
992 if (!tr || tr->destructive()) {
993 return;
996 boost::shared_ptr<Playlist> pl = tr->playlist();
997 if (!pl) {
998 return;
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);
1013 break;
1015 default:
1016 break;
1020 std::string
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
1028 int maxnumber = 0;
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) {
1040 maxnumber = x;
1045 maxnumber++;
1047 char buf[32];
1048 snprintf (buf, sizeof(buf), "%d", maxnumber);
1050 ret = this->name() + "." + route_group()->name () + "." + buf;
1052 return ret;
1055 void
1056 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1058 string name;
1060 boost::shared_ptr<Track> tr = track ();
1061 if (!tr || tr->destructive()) {
1062 return;
1065 boost::shared_ptr<const Playlist> pl = tr->playlist();
1066 if (!pl) {
1067 return;
1070 name = pl->name();
1072 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
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.
1083 if (prompt) {
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);
1097 break;
1099 default:
1100 return;
1104 if (name.length()) {
1105 tr->use_copy_playlist ();
1106 tr->playlist()->set_name (name);
1110 void
1111 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1113 string name;
1115 boost::shared_ptr<Track> tr = track ();
1116 if (!tr || tr->destructive()) {
1117 return;
1120 boost::shared_ptr<const Playlist> pl = tr->playlist();
1121 if (!pl) {
1122 return;
1125 name = pl->name();
1127 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
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);
1136 if (prompt) {
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);
1149 break;
1151 default:
1152 return;
1156 if (name.length()) {
1157 tr->use_new_playlist ();
1158 tr->playlist()->set_name (name);
1162 void
1163 RouteTimeAxisView::clear_playlist ()
1165 boost::shared_ptr<Track> tr = track ();
1166 if (!tr || tr->destructive()) {
1167 return;
1170 boost::shared_ptr<Playlist> pl = tr->playlist();
1171 if (!pl) {
1172 return;
1175 _editor.clear_playlist (pl);
1178 void
1179 RouteTimeAxisView::speed_changed ()
1181 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1184 void
1185 RouteTimeAxisView::update_diskstream_display ()
1187 if (!track()) {
1188 return;
1191 map_frozen ();
1194 void
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 ();
1202 } else {
1203 _editor.select_all_tracks ();
1206 return;
1209 switch (ArdourKeyboard::selection_type (ev->state)) {
1210 case Selection::Toggle:
1211 _editor.get_selection().toggle (this);
1212 break;
1214 case Selection::Set:
1215 _editor.get_selection().set (this);
1216 break;
1218 case Selection::Extend:
1219 _editor.extend_selection_to_track (*this);
1220 break;
1222 case Selection::Add:
1223 _editor.get_selection().add (this);
1224 break;
1228 void
1229 RouteTimeAxisView::set_selected_points (PointSelection& points)
1231 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1232 (*i)->set_selected_points (points);
1236 void
1237 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1239 if (_view) {
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.
1247 void
1248 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1250 double speed = 1.0;
1252 if (track() != 0) {
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);
1272 void
1273 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1275 if (_view) {
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);
1285 return;
1288 RouteGroup*
1289 RouteTimeAxisView::route_group () const
1291 return _route->route_group();
1294 string
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();
1307 } else {
1308 return boost::shared_ptr<Playlist> ();
1312 void
1313 RouteTimeAxisView::name_entry_changed ()
1315 string x;
1317 x = name_entry.get_text ();
1319 if (x == _route->name()) {
1320 return;
1323 strip_whitespace_edges(x);
1325 if (x.length() == 0) {
1326 name_entry.set_text (_route->name());
1327 return;
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"),
1335 PROGRAM_NAME));
1336 name_entry.set_text (_route->name());
1337 } else {
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 ();
1347 if (pl) {
1348 return pl->find_next_region (pos, point, dir);
1351 return boost::shared_ptr<Region> ();
1354 framepos_t
1355 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1357 boost::shared_ptr<Playlist> pl = playlist ();
1359 if (pl) {
1360 return pl->find_next_region_boundary (pos, dir);
1363 return -1;
1366 void
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;
1373 if (tr == 0) {
1374 /* route is a bus, not a track */
1375 return;
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 ();
1392 switch (op) {
1393 case Cut:
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));
1403 break;
1404 case Copy:
1405 if ((what_we_got = playlist->copy (time)) != 0) {
1406 _editor.get_cut_buffer().add (what_we_got);
1408 break;
1410 case Clear:
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 ();
1419 break;
1423 bool
1424 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1426 if (!is_track()) {
1427 return false;
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()) {
1436 return false;
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));
1450 return true;
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();
1460 void
1461 RouteTimeAxisView::build_playlist_menu ()
1463 using namespace Menu_Helpers;
1465 if (!is_track()) {
1466 return;
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 */
1491 PlaylistSorter cmp;
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()) {
1501 item->set_active();
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() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
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)));
1514 } else {
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)));
1528 void
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()) {
1535 return;
1538 boost::shared_ptr<Playlist> pl (wpl.lock());
1540 if (!pl) {
1541 return;
1544 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1546 if (apl) {
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
1550 return;
1552 track()->use_playlist (apl);
1554 RouteGroup* rg = route_group();
1556 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1557 std::string group_string = "." + rg->name() + ".";
1559 std::string take_name = apl->name();
1560 std::string::size_type idx = take_name.find(group_string);
1562 if (idx == std::string::npos)
1563 return;
1565 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1567 boost::shared_ptr<RouteList> rl (rg->route_list());
1569 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1570 if ( (*i) == this->route()) {
1571 continue;
1574 std::string playlist_name = (*i)->name()+group_string+take_name;
1576 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1577 if (!track) {
1578 continue;
1581 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1582 if (!ipl) {
1583 // No playlist for this track for this take yet, make it
1584 track->use_new_playlist();
1585 track->playlist()->set_name(playlist_name);
1586 } else {
1587 track->use_playlist(ipl);
1594 void
1595 RouteTimeAxisView::show_playlist_selector ()
1597 _editor.playlist_selector().show_for (this);
1600 void
1601 RouteTimeAxisView::map_frozen ()
1603 if (!is_track()) {
1604 return;
1607 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1609 switch (track()->freeze_state()) {
1610 case Track::Frozen:
1611 playlist_button.set_sensitive (false);
1612 rec_enable_button->set_sensitive (false);
1613 break;
1614 default:
1615 playlist_button.set_sensitive (true);
1616 rec_enable_button->set_sensitive (true);
1617 break;
1621 void
1622 RouteTimeAxisView::color_handler ()
1624 //case cTimeStretchOutline:
1625 if (timestretch_rect) {
1626 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1628 //case cTimeStretchFill:
1629 if (timestretch_rect) {
1630 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1633 reset_meter();
1636 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1637 * Will add track if necessary.
1639 void
1640 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1642 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1643 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1645 if (!track) {
1646 /* it doesn't exist yet, so we don't care about the button state: just add it */
1647 create_automation_child (param, true);
1648 } else {
1649 assert (menu);
1650 bool yn = menu->get_active();
1651 if (track->set_visibility (menu->get_active()) && yn) {
1653 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1654 will have done that for us.
1657 if (!no_redraw) {
1658 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1664 void
1665 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1667 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1669 if (!track) {
1670 return;
1673 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1675 // if Evoral::Parameter::operator< doesn't obey strict weak ordering, we may crash here....
1676 track->get_state_node()->add_property (X_("shown"), X_("no"));
1678 if (menu && !_hidden) {
1679 ignore_toggle = true;
1680 menu->set_active (false);
1681 ignore_toggle = false;
1684 if (_route && !no_redraw) {
1685 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1690 void
1691 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1693 if (apply_to_selection) {
1694 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1695 } else {
1696 no_redraw = true;
1698 /* Show our automation */
1700 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1701 i->second->set_marked_for_display (true);
1702 i->second->canvas_display()->show();
1703 i->second->get_state_node()->add_property ("shown", X_("yes"));
1705 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1707 if (menu) {
1708 menu->set_active(true);
1713 /* Show processor automation */
1715 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1716 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1717 if ((*ii)->view == 0) {
1718 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1721 (*ii)->menu_item->set_active (true);
1725 no_redraw = false;
1727 /* Redraw */
1729 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1733 void
1734 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1736 if (apply_to_selection) {
1737 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1738 } else {
1739 no_redraw = true;
1741 /* Show our automation */
1743 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1744 if (i->second->has_automation()) {
1745 i->second->set_marked_for_display (true);
1746 i->second->canvas_display()->show();
1747 i->second->get_state_node()->add_property ("shown", X_("yes"));
1749 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1750 if (menu) {
1751 menu->set_active(true);
1757 /* Show processor automation */
1759 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1760 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1761 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1762 (*ii)->menu_item->set_active (true);
1767 no_redraw = false;
1769 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1773 void
1774 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1776 if (apply_to_selection) {
1777 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1778 } else {
1779 no_redraw = true;
1781 /* Hide our automation */
1783 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1784 i->second->set_marked_for_display (false);
1785 i->second->hide ();
1786 i->second->get_state_node()->add_property ("shown", X_("no"));
1788 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1790 if (menu) {
1791 menu->set_active (false);
1795 /* Hide processor automation */
1797 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1798 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1799 (*ii)->menu_item->set_active (false);
1803 no_redraw = false;
1804 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1809 void
1810 RouteTimeAxisView::region_view_added (RegionView* rv)
1812 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1813 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1814 boost::shared_ptr<AutomationTimeAxisView> atv;
1816 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1817 atv->add_ghost(rv);
1821 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1822 (*i)->add_ghost(rv);
1826 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1828 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1829 delete *i;
1834 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1836 parent.remove_processor_automation_node (this);
1839 void
1840 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1842 if (pan->view) {
1843 remove_child (pan->view);
1847 RouteTimeAxisView::ProcessorAutomationNode*
1848 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1850 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1852 if ((*i)->processor == processor) {
1854 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1855 if ((*ii)->what == what) {
1856 return *ii;
1862 return 0;
1865 static string
1866 legalize_for_xml_node (string str)
1868 string::size_type pos;
1869 string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_=:";
1870 string legal;
1872 legal = str;
1873 pos = 0;
1875 while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
1876 legal.replace (pos, 1, "_");
1877 pos += 1;
1880 return legal;
1884 void
1885 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1887 string name;
1888 ProcessorAutomationNode* pan;
1890 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1891 /* session state may never have been saved with new plugin */
1892 error << _("programming error: ")
1893 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1894 processor->name(), what.type(), (int) what.channel(), what.id() )
1895 << endmsg;
1896 /*NOTREACHED*/
1897 return;
1900 if (pan->view) {
1901 return;
1904 name = processor->describe_parameter (what);
1906 /* create a string that is a legal XML node name that can be used to refer to this redirect+port combination */
1908 /* FIXME: ew */
1910 char state_name[256];
1911 snprintf (state_name, sizeof (state_name), "%s-%" PRIu32, legalize_for_xml_node (processor->name()).c_str(), what.id());
1913 boost::shared_ptr<AutomationControl> control
1914 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1916 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1917 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1918 _editor, *this, false, parent_canvas, name, state_name));
1920 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1922 if (!pan->view->marked_for_display()) {
1923 pan->view->hide ();
1924 } else {
1925 pan->menu_item->set_active (true);
1928 add_child (pan->view);
1930 if (_view) {
1931 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1934 processor->mark_automation_visible (what, true);
1937 void
1938 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1940 if (!_hidden) {
1941 pan->menu_item->set_active (false);
1944 i->mark_automation_visible (pan->what, false);
1946 if (!no_redraw) {
1947 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1951 void
1952 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1954 boost::shared_ptr<Processor> processor (p.lock ());
1955 if (!processor) {
1956 return;
1959 set<Evoral::Parameter> s;
1960 boost::shared_ptr<AutomationLine> al;
1962 processor->what_has_visible_data (s);
1964 for (set<Evoral::Parameter>::iterator i = s.begin(); i != s.end(); ++i) {
1966 if ((al = find_processor_automation_curve (processor, *i)) != 0) {
1967 al->queue_reset ();
1968 } else {
1969 add_processor_automation_curve (processor, (*i));
1974 void
1975 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1977 using namespace Menu_Helpers;
1979 XMLProperty* prop;
1980 XMLNode* node;
1982 add_child (track);
1984 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1986 bool hideit = (!show);
1988 if ((node = track->get_state_node()) != 0) {
1989 if ((prop = node->property ("shown")) != 0) {
1990 if (string_is_affirmative (prop->value())) {
1991 hideit = false;
1996 _automation_tracks[param] = track;
1998 track->set_visibility (!hideit);
2000 if (!no_redraw) {
2001 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2004 if (!EventTypeMap::instance().is_midi_parameter(param)) {
2005 /* MIDI-related parameters are always in the menu, there's no
2006 reason to rebuild the menu just because we added a automation
2007 lane for one of them. But if we add a non-MIDI automation
2008 lane, then we need to invalidate the display menu.
2010 delete display_menu;
2011 display_menu = 0;
2015 void
2016 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
2018 boost::shared_ptr<Processor> processor (p.lock ());
2020 if (!processor || !processor->display_to_user ()) {
2021 return;
2024 using namespace Menu_Helpers;
2025 ProcessorAutomationInfo *rai;
2026 list<ProcessorAutomationInfo*>::iterator x;
2028 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
2029 std::set<Evoral::Parameter> has_visible_automation;
2031 processor->what_has_visible_data(has_visible_automation);
2033 if (automatable.empty()) {
2034 return;
2037 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
2038 if ((*x)->processor == processor) {
2039 break;
2043 if (x == processor_automation.end()) {
2045 rai = new ProcessorAutomationInfo (processor);
2046 processor_automation.push_back (rai);
2048 } else {
2050 rai = *x;
2054 /* any older menu was deleted at the top of processors_changed()
2055 when we cleared the subplugin menu.
2058 rai->menu = manage (new Menu);
2059 MenuList& items = rai->menu->items();
2060 rai->menu->set_name ("ArdourContextMenu");
2062 items.clear ();
2064 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2066 ProcessorAutomationNode* pan;
2067 CheckMenuItem* mitem;
2069 string name = processor->describe_parameter (*i);
2071 items.push_back (CheckMenuElem (name));
2072 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2074 _subplugin_menu_map[*i] = mitem;
2076 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2077 mitem->set_active(true);
2080 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2082 /* new item */
2084 pan = new ProcessorAutomationNode (*i, mitem, *this);
2086 rai->lines.push_back (pan);
2088 } else {
2090 pan->menu_item = mitem;
2094 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2097 /* add the menu for this processor, because the subplugin
2098 menu is always cleared at the top of processors_changed().
2099 this is the result of some poor design in gtkmm and/or
2100 GTK+.
2103 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2104 rai->valid = true;
2107 void
2108 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2109 RouteTimeAxisView::ProcessorAutomationNode* pan)
2111 bool showit = pan->menu_item->get_active();
2112 bool redraw = false;
2114 if (pan->view == 0 && showit) {
2115 add_processor_automation_curve (rai->processor, pan->what);
2116 redraw = true;
2119 if (pan->view && showit != pan->view->marked_for_display()) {
2121 if (showit) {
2122 pan->view->set_marked_for_display (true);
2123 pan->view->canvas_display()->show();
2124 pan->view->canvas_background()->show();
2125 } else {
2126 rai->processor->mark_automation_visible (pan->what, true);
2127 pan->view->set_marked_for_display (false);
2128 pan->view->hide ();
2131 redraw = true;
2135 if (redraw && !no_redraw) {
2136 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2141 void
2142 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2144 if (c.type == RouteProcessorChange::MeterPointChange) {
2145 /* nothing to do if only the meter point has changed */
2146 return;
2149 using namespace Menu_Helpers;
2151 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2152 (*i)->valid = false;
2155 _subplugin_menu_map.clear ();
2156 subplugin_menu.items().clear ();
2158 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2159 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2161 bool deleted_processor_automation = false;
2163 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2165 list<ProcessorAutomationInfo*>::iterator tmp;
2167 tmp = i;
2168 ++tmp;
2170 if (!(*i)->valid) {
2172 delete *i;
2173 processor_automation.erase (i);
2174 deleted_processor_automation = true;
2178 i = tmp;
2181 if (deleted_processor_automation && !no_redraw) {
2182 _route->gui_changed ("track_height", this);
2186 boost::shared_ptr<AutomationLine>
2187 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2189 ProcessorAutomationNode* pan;
2191 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2192 if (pan->view) {
2193 pan->view->line();
2197 return boost::shared_ptr<AutomationLine>();
2200 void
2201 RouteTimeAxisView::reset_processor_automation_curves ()
2203 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2204 (*i)->reset();
2208 void
2209 RouteTimeAxisView::update_rec_display ()
2211 RouteUI::update_rec_display ();
2212 name_entry.set_sensitive (!_route->record_enabled());
2215 void
2216 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2218 if (apply_to_selection) {
2219 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2220 } else {
2222 if (_view) {
2223 _view->set_layer_display (d);
2226 ensure_xml_node ();
2227 xml_node->add_property (N_("layer-display"), enum_2_string (d));
2231 LayerDisplay
2232 RouteTimeAxisView::layer_display () const
2234 if (_view) {
2235 return _view->layer_display ();
2238 /* we don't know, since we don't have a _view, so just return something */
2239 return Overlaid;
2244 boost::shared_ptr<AutomationTimeAxisView>
2245 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2247 AutomationTracks::iterator i = _automation_tracks.find(param);
2248 if (i != _automation_tracks.end()) {
2249 return i->second;
2250 } else {
2251 return boost::shared_ptr<AutomationTimeAxisView>();
2255 void
2256 RouteTimeAxisView::fast_update ()
2258 gm.get_level_meter().update_meters ();
2261 void
2262 RouteTimeAxisView::hide_meter ()
2264 clear_meter ();
2265 gm.get_level_meter().hide_meters ();
2268 void
2269 RouteTimeAxisView::show_meter ()
2271 reset_meter ();
2274 void
2275 RouteTimeAxisView::reset_meter ()
2277 if (Config->get_show_track_meters()) {
2278 gm.get_level_meter().setup_meters (height-5);
2279 } else {
2280 hide_meter ();
2284 void
2285 RouteTimeAxisView::clear_meter ()
2287 gm.get_level_meter().clear_meters ();
2290 void
2291 RouteTimeAxisView::meter_changed ()
2293 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2294 reset_meter();
2297 void
2298 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2300 reset_meter ();
2303 void
2304 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2306 using namespace Menu_Helpers;
2308 if (!_underlay_streams.empty()) {
2309 MenuList& parent_items = parent_menu->items();
2310 Menu* gs_menu = manage (new Menu);
2311 gs_menu->set_name ("ArdourContextMenu");
2312 MenuList& gs_items = gs_menu->items();
2314 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2316 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2317 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2318 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2323 bool
2324 RouteTimeAxisView::set_underlay_state()
2326 if (!underlay_xml_node) {
2327 return false;
2330 XMLNodeList nlist = underlay_xml_node->children();
2331 XMLNodeConstIterator niter;
2332 XMLNode *child_node;
2334 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2335 child_node = *niter;
2337 if (child_node->name() != "Underlay") {
2338 continue;
2341 XMLProperty* prop = child_node->property ("id");
2342 if (prop) {
2343 PBD::ID id (prop->value());
2345 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2347 if (v) {
2348 add_underlay(v->view(), false);
2353 return false;
2356 void
2357 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2359 if (!v) {
2360 return;
2363 RouteTimeAxisView& other = v->trackview();
2365 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2366 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2367 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2368 /*NOTREACHED*/
2371 _underlay_streams.push_back(v);
2372 other._underlay_mirrors.push_back(this);
2374 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2376 if (update_xml) {
2377 if (!underlay_xml_node) {
2378 ensure_xml_node();
2379 underlay_xml_node = xml_node->add_child("Underlays");
2382 XMLNode* node = underlay_xml_node->add_child("Underlay");
2383 XMLProperty* prop = node->add_property("id");
2384 prop->set_value(v->trackview().route()->id().to_s());
2389 void
2390 RouteTimeAxisView::remove_underlay (StreamView* v)
2392 if (!v) {
2393 return;
2396 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2397 RouteTimeAxisView& other = v->trackview();
2399 if (it != _underlay_streams.end()) {
2400 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2402 if (gm == other._underlay_mirrors.end()) {
2403 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2404 /*NOTREACHED*/
2407 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2409 _underlay_streams.erase(it);
2410 other._underlay_mirrors.erase(gm);
2412 if (underlay_xml_node) {
2413 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2418 void
2419 RouteTimeAxisView::set_button_names ()
2421 rec_enable_button_label.set_text (_("r"));
2423 if (_route && _route->solo_safe()) {
2424 solo_button_label.set_text (X_("!"));
2425 } else {
2426 if (Config->get_solo_control_is_listen_control()) {
2427 switch (Config->get_listen_position()) {
2428 case AfterFaderListen:
2429 solo_button_label.set_text (_("A"));
2430 break;
2431 case PreFaderListen:
2432 solo_button_label.set_text (_("P"));
2433 break;
2435 } else {
2436 solo_button_label.set_text (_("s"));
2439 mute_button_label.set_text (_("m"));
2442 Gtk::CheckMenuItem*
2443 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2445 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2446 if (i != _main_automation_menu_map.end()) {
2447 return i->second;
2450 i = _subplugin_menu_map.find (param);
2451 if (i != _subplugin_menu_map.end()) {
2452 return i->second;
2455 return 0;
2458 void
2459 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2461 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2462 if (!c) {
2463 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2464 return;
2467 gain_track.reset (new AutomationTimeAxisView (_session,
2468 _route, _route->amp(), c, param,
2469 _editor,
2470 *this,
2471 false,
2472 parent_canvas,
2473 _route->amp()->describe_parameter(param)));
2475 if (_view) {
2476 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2479 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2482 static
2483 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2485 l->push_back (rv->region());
2488 RegionView*
2489 RouteTimeAxisView::combine_regions ()
2491 assert (is_track());
2493 if (!_view) {
2494 return 0;
2497 Playlist::RegionList selected_regions;
2498 boost::shared_ptr<Playlist> playlist = track()->playlist();
2500 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2502 playlist->clear_changes ();
2503 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2505 _session->add_command (new StatefulDiffCommand (playlist));
2506 /* make the new region be selected */
2508 return _view->find_view (compound_region);
2511 void
2512 RouteTimeAxisView::uncombine_regions ()
2514 assert (is_track());
2516 if (!_view) {
2517 return;
2520 Playlist::RegionList selected_regions;
2521 boost::shared_ptr<Playlist> playlist = track()->playlist();
2523 /* have to grab selected regions first because the uncombine is going
2524 * to change that in the middle of the list traverse
2527 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2529 playlist->clear_changes ();
2531 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2532 playlist->uncombine (*i);
2535 _session->add_command (new StatefulDiffCommand (playlist));