Add a couple of missing attach_buffers() calls after _ports has been changed. I...
[ardour2.git] / gtk2_ardour / route_time_axis.cc
blob85601362c8207f37edb9d866c86842f05db35050
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, Canvas& canvas)
108 : AxisView(sess)
109 , RouteUI(sess)
110 , TimeAxisView(sess,ed,(TimeAxisView*) 0, canvas)
111 , _view (0)
112 , parent_canvas (canvas)
113 , button_table (3, 3)
114 , route_group_button (_("g"))
115 , playlist_button (_("p"))
116 , automation_button (_("a"))
117 , automation_action_menu (0)
118 , plugins_submenu_item (0)
119 , route_group_menu (0)
120 , playlist_action_menu (0)
121 , mode_menu (0)
122 , color_mode_menu (0)
123 , gm (sess, slider, true, 115)
127 void
128 RouteTimeAxisView::set_route (boost::shared_ptr<Route> rt)
130 RouteUI::set_route (rt);
132 gm.set_controls (_route, _route->shared_peak_meter(), _route->amp());
133 gm.get_level_meter().set_no_show_all();
134 gm.get_level_meter().setup_meters(50);
136 string str = gui_property ("height");
137 if (!str.empty()) {
138 set_height (atoi (str));
139 } else {
140 set_height (preset_height (HeightNormal));
143 if (!_route->is_hidden()) {
144 if (gui_property ("visible").empty()) {
145 set_gui_property ("visible", true);
147 } else {
148 set_gui_property ("visible", false);
151 mute_changed (0);
152 update_solo_display ();
154 timestretch_rect = 0;
155 no_redraw = false;
157 ignore_toggle = false;
159 route_group_button.set_name ("TrackGroupButton");
160 playlist_button.set_name ("TrackPlaylistButton");
161 automation_button.set_name ("TrackAutomationButton");
163 route_group_button.unset_flags (Gtk::CAN_FOCUS);
164 playlist_button.unset_flags (Gtk::CAN_FOCUS);
165 automation_button.unset_flags (Gtk::CAN_FOCUS);
167 route_group_button.signal_button_release_event().connect (sigc::mem_fun(*this, &RouteTimeAxisView::route_group_click), false);
168 playlist_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::playlist_click));
169 automation_button.signal_clicked().connect (sigc::mem_fun(*this, &RouteTimeAxisView::automation_click));
171 if (is_track()) {
173 /* use icon */
175 rec_enable_button->remove ();
177 switch (track()->mode()) {
178 case ARDOUR::Normal:
179 case ARDOUR::NonLayered:
180 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
181 break;
182 case ARDOUR::Destructive:
183 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
184 break;
186 rec_enable_button->show_all ();
188 controls_table.attach (*rec_enable_button, 5, 6, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
190 if (is_midi_track()) {
191 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record (Right-click for Step Edit)"));
192 } else {
193 ARDOUR_UI::instance()->set_tip(*rec_enable_button, _("Record"));
196 rec_enable_button->set_sensitive (_session->writable());
199 controls_hbox.pack_start(gm.get_level_meter(), false, false);
200 _route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
201 _route->input()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
202 _route->output()->changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
204 controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
206 if (!_route->is_master()) {
207 controls_table.attach (*solo_button, 7, 8, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
210 controls_table.attach (route_group_button, 7, 8, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
211 controls_table.attach (gm.get_gain_slider(), 0, 5, 1, 2, Gtk::SHRINK, Gtk::SHRINK, 0, 0);
213 ARDOUR_UI::instance()->set_tip(*solo_button,_("Solo"));
214 ARDOUR_UI::instance()->set_tip(*mute_button,_("Mute"));
215 ARDOUR_UI::instance()->set_tip(route_group_button, _("Route Group"));
216 ARDOUR_UI::instance()->set_tip(playlist_button,_("Playlist"));
217 ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
219 label_view ();
221 controls_table.attach (automation_button, 6, 7, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
223 if (is_track() && track()->mode() == ARDOUR::Normal) {
224 controls_table.attach (playlist_button, 5, 6, 1, 2, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND);
227 _y_position = -1;
229 _route->processors_changed.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::processors_changed, this, _1), gui_context());
230 _route->PropertyChanged.connect (*this, invalidator (*this), ui_bind (&RouteTimeAxisView::route_property_changed, this, _1), gui_context());
232 if (is_track()) {
234 str = gui_property ("layer-display");
235 if (!str.empty()) {
236 set_layer_display (LayerDisplay (string_2_enum (str, _view->layer_display ())));
239 track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::map_frozen, this), gui_context());
240 track()->SpeedChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::speed_changed, this), gui_context());
242 /* pick up the correct freeze state */
243 map_frozen ();
246 _editor.ZoomChanged.connect (sigc::mem_fun(*this, &RouteTimeAxisView::reset_samples_per_unit));
247 _editor.HorizontalPositionChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::horizontal_position_changed));
248 ColorsChanged.connect (sigc::mem_fun (*this, &RouteTimeAxisView::color_handler));
250 PropertyList* plist = new PropertyList();
252 plist->add (ARDOUR::Properties::edit, true);
253 plist->add (ARDOUR::Properties::mute, true);
254 plist->add (ARDOUR::Properties::solo, true);
256 route_group_menu = new RouteGroupMenu (_session, plist);
258 gm.get_gain_slider().signal_scroll_event().connect(sigc::mem_fun(*this, &RouteTimeAxisView::controls_ebox_scroll), false);
259 gm.get_gain_slider().set_name ("TrackGainFader");
261 show_name_entry ();
262 hide_name_label ();
265 RouteTimeAxisView::~RouteTimeAxisView ()
267 CatchDeletion (this);
269 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
270 delete *i;
273 delete playlist_action_menu;
274 playlist_action_menu = 0;
276 delete _view;
277 _view = 0;
279 _automation_tracks.clear ();
281 delete route_group_menu;
284 void
285 RouteTimeAxisView::post_construct ()
287 /* map current state of the route */
289 update_diskstream_display ();
290 setup_processor_menu_and_curves ();
291 reset_processor_automation_curves ();
294 /** Set up the processor menu for the current set of processors, and
295 * display automation curves for any parameters which have data.
297 void
298 RouteTimeAxisView::setup_processor_menu_and_curves ()
300 _subplugin_menu_map.clear ();
301 subplugin_menu.items().clear ();
302 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
303 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
306 gint
307 RouteTimeAxisView::route_group_click (GdkEventButton *ev)
309 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
310 if (_route->route_group()) {
311 _route->route_group()->remove (_route);
313 return false;
316 WeakRouteList r;
317 r.push_back (route ());
319 route_group_menu->build (r);
320 route_group_menu->menu()->popup (ev->button, ev->time);
322 return false;
325 void
326 RouteTimeAxisView::playlist_changed ()
328 label_view ();
331 void
332 RouteTimeAxisView::label_view ()
334 string x = _route->name();
336 if (x != name_entry.get_text()) {
337 name_entry.set_text (x);
340 if (x != name_label.get_text()) {
341 name_label.set_text (x);
344 ARDOUR_UI::instance()->set_tip (name_entry, x);
347 void
348 RouteTimeAxisView::route_property_changed (const PropertyChange& what_changed)
350 if (what_changed.contains (ARDOUR::Properties::name)) {
351 label_view ();
355 void
356 RouteTimeAxisView::take_name_changed (void *src)
358 if (src != this) {
359 label_view ();
363 void
364 RouteTimeAxisView::playlist_click ()
366 build_playlist_menu ();
367 conditionally_add_to_selection ();
368 playlist_action_menu->popup (1, gtk_get_current_event_time());
371 void
372 RouteTimeAxisView::automation_click ()
374 conditionally_add_to_selection ();
375 build_automation_action_menu (false);
376 automation_action_menu->popup (1, gtk_get_current_event_time());
379 void
380 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
382 using namespace Menu_Helpers;
384 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
385 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
388 detach_menu (subplugin_menu);
390 _main_automation_menu_map.clear ();
391 delete automation_action_menu;
392 automation_action_menu = new Menu;
394 MenuList& items = automation_action_menu->items();
396 automation_action_menu->set_name ("ArdourContextMenu");
398 items.push_back (MenuElem (_("Show All Automation"),
399 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
401 items.push_back (MenuElem (_("Show Existing Automation"),
402 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
404 items.push_back (MenuElem (_("Hide All Automation"),
405 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
407 items.push_back (SeparatorElem ());
409 /* Attach the plugin submenu. It may have previously been used elsewhere,
410 so it was detached above */
412 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
413 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
416 void
417 RouteTimeAxisView::build_display_menu ()
419 using namespace Menu_Helpers;
421 /* prepare it */
423 TimeAxisView::build_display_menu ();
425 /* now fill it with our stuff */
427 MenuList& items = display_menu->items();
428 display_menu->set_name ("ArdourContextMenu");
430 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
432 if (_size_menu) {
433 detach_menu (*_size_menu);
435 build_size_menu ();
436 items.push_back (MenuElem (_("Height"), *_size_menu));
438 items.push_back (SeparatorElem());
440 if (!Profile->get_sae()) {
441 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
442 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
443 items.push_back (SeparatorElem());
446 // Hook for derived classes to add type specific stuff
447 append_extra_display_menu_items ();
449 if (is_track()) {
451 Menu* layers_menu = manage (new Menu);
452 MenuList &layers_items = layers_menu->items();
453 layers_menu->set_name("ArdourContextMenu");
455 RadioMenuItem::Group layers_group;
457 /* Find out how many overlaid/stacked tracks we have in the selection */
459 int overlaid = 0;
460 int stacked = 0;
461 TrackSelection const & s = _editor.get_selection().tracks;
462 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
463 StreamView* v = (*i)->view ();
464 if (!v) {
465 continue;
468 switch (v->layer_display ()) {
469 case Overlaid:
470 ++overlaid;
471 break;
472 case Stacked:
473 ++stacked;
474 break;
478 /* We're not connecting to signal_toggled() here; in the case where these two items are
479 set to be in the `inconsistent' state, it seems that one or other will end up active
480 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
481 select the active one, no toggled signal is emitted so nothing happens.
484 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
485 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
486 i->set_active (overlaid != 0 && stacked == 0);
487 i->set_inconsistent (overlaid != 0 && stacked != 0);
488 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
490 layers_items.push_back (
491 RadioMenuElem (layers_group, _("Stacked"),
492 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
495 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
496 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
497 i->set_active (overlaid == 0 && stacked != 0);
498 i->set_inconsistent (overlaid != 0 && stacked != 0);
500 items.push_back (MenuElem (_("Layers"), *layers_menu));
502 if (!Profile->get_sae()) {
504 Menu* alignment_menu = manage (new Menu);
505 MenuList& alignment_items = alignment_menu->items();
506 alignment_menu->set_name ("ArdourContextMenu");
508 RadioMenuItem::Group align_group;
510 /* Same verbose hacks as for the layering options above */
512 int existing = 0;
513 int capture = 0;
514 int automatic = 0;
515 int styles = 0;
516 boost::shared_ptr<Track> first_track;
518 TrackSelection const & s = _editor.get_selection().tracks;
519 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
520 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
521 if (!r || !r->is_track ()) {
522 continue;
525 if (!first_track) {
526 first_track = r->track();
529 switch (r->track()->alignment_choice()) {
530 case Automatic:
531 ++automatic;
532 styles |= 0x1;
533 switch (r->track()->alignment_style()) {
534 case ExistingMaterial:
535 ++existing;
536 break;
537 case CaptureTime:
538 ++capture;
539 break;
541 break;
542 case UseExistingMaterial:
543 ++existing;
544 styles |= 0x2;
545 break;
546 case UseCaptureTime:
547 ++capture;
548 styles |= 0x4;
549 break;
553 bool inconsistent;
554 switch (styles) {
555 case 1:
556 case 2:
557 case 4:
558 inconsistent = false;
559 break;
560 default:
561 inconsistent = true;
562 break;
565 RadioMenuItem* i;
567 if (!inconsistent && first_track) {
569 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
570 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
571 i->set_active (automatic != 0 && existing == 0 && capture == 0);
572 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
574 switch (first_track->alignment_choice()) {
575 case Automatic:
576 switch (first_track->alignment_style()) {
577 case ExistingMaterial:
578 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
579 break;
580 case CaptureTime:
581 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
582 break;
584 break;
585 default:
586 break;
589 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
590 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
591 i->set_active (existing != 0 && capture == 0 && automatic == 0);
592 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
594 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
595 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
596 i->set_active (existing == 0 && capture != 0 && automatic == 0);
597 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
599 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
601 } else {
602 /* show nothing */
605 Menu* mode_menu = manage (new Menu);
606 MenuList& mode_items = mode_menu->items ();
607 mode_menu->set_name ("ArdourContextMenu");
609 RadioMenuItem::Group mode_group;
611 int normal = 0;
612 int tape = 0;
613 int non_layered = 0;
615 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
616 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
617 if (!r || !r->is_track ()) {
618 continue;
621 switch (r->track()->mode()) {
622 case Normal:
623 ++normal;
624 break;
625 case Destructive:
626 ++tape;
627 break;
628 case NonLayered:
629 ++non_layered;
630 break;
634 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
635 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
636 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
637 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
638 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
640 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
641 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
642 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
643 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
644 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
646 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
647 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
648 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
649 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
650 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
652 items.push_back (MenuElem (_("Mode"), *mode_menu));
655 color_mode_menu = build_color_mode_menu();
656 if (color_mode_menu) {
657 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
660 items.push_back (SeparatorElem());
662 build_playlist_menu ();
663 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
664 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
666 route_group_menu->detach ();
668 WeakRouteList r;
669 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
670 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
671 if (rtv) {
672 r.push_back (rtv->route ());
676 if (r.empty ()) {
677 r.push_back (route ());
680 route_group_menu->build (r);
681 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
683 build_automation_action_menu (true);
684 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
686 items.push_back (SeparatorElem());
689 int active = 0;
690 int inactive = 0;
691 TrackSelection const & s = _editor.get_selection().tracks;
692 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
693 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
694 if (!r) {
695 continue;
698 if (r->route()->active()) {
699 ++active;
700 } else {
701 ++inactive;
705 items.push_back (CheckMenuElem (_("Active")));
706 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
707 bool click_sets_active = true;
708 if (active > 0 && inactive == 0) {
709 i->set_active (true);
710 click_sets_active = false;
711 } else if (active > 0 && inactive > 0) {
712 i->set_inconsistent (true);
714 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
716 items.push_back (SeparatorElem());
717 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
718 if (!Profile->get_sae()) {
719 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
720 } else {
721 items.push_front (SeparatorElem());
722 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
726 void
727 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
729 if (apply_to_selection) {
730 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
731 } else {
733 bool needs_bounce;
735 if (!track()->can_use_mode (mode, needs_bounce)) {
737 if (!needs_bounce) {
738 /* cannot be done */
739 return;
740 } else {
741 cerr << "would bounce this one\n";
742 return;
746 track()->set_mode (mode);
748 rec_enable_button->remove ();
750 switch (mode) {
751 case ARDOUR::NonLayered:
752 case ARDOUR::Normal:
753 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
754 break;
755 case ARDOUR::Destructive:
756 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
757 break;
760 rec_enable_button->show_all ();
764 void
765 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
767 double x1;
768 double x2;
769 double y2;
771 TimeAxisView::show_timestretch (start, end);
773 hide_timestretch ();
775 #if 0
776 if (ts.empty()) {
777 return;
781 /* check that the time selection was made in our route, or our route group.
782 remember that route_group() == 0 implies the route is *not* in a edit group.
785 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
786 /* this doesn't apply to us */
787 return;
790 /* ignore it if our edit group is not active */
792 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
793 return;
795 #endif
797 if (timestretch_rect == 0) {
798 timestretch_rect = new SimpleRect (*canvas_display ());
799 timestretch_rect->property_x1() = 0.0;
800 timestretch_rect->property_y1() = 0.0;
801 timestretch_rect->property_x2() = 0.0;
802 timestretch_rect->property_y2() = 0.0;
803 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
804 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
807 timestretch_rect->show ();
808 timestretch_rect->raise_to_top ();
810 x1 = start / _editor.get_current_zoom();
811 x2 = (end - 1) / _editor.get_current_zoom();
812 y2 = current_height() - 2;
814 timestretch_rect->property_x1() = x1;
815 timestretch_rect->property_y1() = 1.0;
816 timestretch_rect->property_x2() = x2;
817 timestretch_rect->property_y2() = y2;
820 void
821 RouteTimeAxisView::hide_timestretch ()
823 TimeAxisView::hide_timestretch ();
825 if (timestretch_rect) {
826 timestretch_rect->hide ();
830 void
831 RouteTimeAxisView::show_selection (TimeSelection& ts)
834 #if 0
835 /* ignore it if our edit group is not active or if the selection was started
836 in some other track or route group (remember that route_group() == 0 means
837 that the track is not in an route group).
840 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
841 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
842 hide_selection ();
843 return;
845 #endif
847 TimeAxisView::show_selection (ts);
850 void
851 RouteTimeAxisView::set_height (uint32_t h)
853 int gmlen = h - 5;
854 bool height_changed = (height == 0) || (h != height);
855 gm.get_level_meter().setup_meters (gmlen);
857 TimeAxisView::set_height (h);
859 if (_view) {
860 _view->set_height ((double) current_height());
863 if (height >= preset_height (HeightNormal)) {
865 reset_meter();
867 gm.get_gain_slider().show();
868 mute_button->show();
869 if (!_route || _route->is_monitor()) {
870 solo_button->hide();
871 } else {
872 solo_button->show();
874 if (rec_enable_button)
875 rec_enable_button->show();
877 route_group_button.show();
878 automation_button.show();
880 if (is_track() && track()->mode() == ARDOUR::Normal) {
881 playlist_button.show();
884 } else {
886 reset_meter();
888 gm.get_gain_slider().hide();
889 mute_button->show();
890 if (!_route || _route->is_monitor()) {
891 solo_button->hide();
892 } else {
893 solo_button->show();
895 if (rec_enable_button)
896 rec_enable_button->show();
898 route_group_button.hide ();
899 automation_button.hide ();
901 if (is_track() && track()->mode() == ARDOUR::Normal) {
902 playlist_button.hide ();
907 if (height_changed && !no_redraw) {
908 /* only emit the signal if the height really changed */
909 request_redraw ();
913 void
914 RouteTimeAxisView::set_color (Gdk::Color const & c)
916 RouteUI::set_color (c);
918 if (_view) {
919 _view->apply_color (_color, StreamView::RegionColor);
923 void
924 RouteTimeAxisView::reset_samples_per_unit ()
926 set_samples_per_unit (_editor.get_current_zoom());
929 void
930 RouteTimeAxisView::horizontal_position_changed ()
932 if (_view) {
933 _view->horizontal_position_changed ();
937 void
938 RouteTimeAxisView::set_samples_per_unit (double spu)
940 double speed = 1.0;
942 if (track()) {
943 speed = track()->speed();
946 if (_view) {
947 _view->set_samples_per_unit (spu * speed);
950 TimeAxisView::set_samples_per_unit (spu * speed);
953 void
954 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
956 if (!mitem->get_active()) {
957 /* this is one of the two calls made when these radio menu items change status. this one
958 is for the item that became inactive, and we want to ignore it.
960 return;
963 if (apply_to_selection) {
964 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
965 } else {
966 if (track ()) {
967 track()->set_align_choice (choice);
972 void
973 RouteTimeAxisView::rename_current_playlist ()
975 ArdourPrompter prompter (true);
976 string name;
978 boost::shared_ptr<Track> tr = track();
979 if (!tr || tr->destructive()) {
980 return;
983 boost::shared_ptr<Playlist> pl = tr->playlist();
984 if (!pl) {
985 return;
988 prompter.set_title (_("Rename Playlist"));
989 prompter.set_prompt (_("New name for playlist:"));
990 prompter.set_initial_text (pl->name());
991 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
992 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
994 switch (prompter.run ()) {
995 case Gtk::RESPONSE_ACCEPT:
996 prompter.get_result (name);
997 if (name.length()) {
998 pl->set_name (name);
1000 break;
1002 default:
1003 break;
1007 std::string
1008 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1010 std::string ret (basename);
1012 std::string const group_string = "." + route_group()->name() + ".";
1014 // iterate through all playlists
1015 int maxnumber = 0;
1016 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1017 std::string tmp = (*i)->name();
1019 std::string::size_type idx = tmp.find(group_string);
1020 // find those which belong to this group
1021 if (idx != string::npos) {
1022 tmp = tmp.substr(idx + group_string.length());
1024 // and find the largest current number
1025 int x = atoi(tmp.c_str());
1026 if (x > maxnumber) {
1027 maxnumber = x;
1032 maxnumber++;
1034 char buf[32];
1035 snprintf (buf, sizeof(buf), "%d", maxnumber);
1037 ret = this->name() + "." + route_group()->name () + "." + buf;
1039 return ret;
1042 void
1043 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1045 string name;
1047 boost::shared_ptr<Track> tr = track ();
1048 if (!tr || tr->destructive()) {
1049 return;
1052 boost::shared_ptr<const Playlist> pl = tr->playlist();
1053 if (!pl) {
1054 return;
1057 name = pl->name();
1059 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1060 name = resolve_new_group_playlist_name(name, playlists_before_op);
1063 while (_session->playlists->by_name(name)) {
1064 name = Playlist::bump_name (name, *_session);
1067 // TODO: The prompter "new" button should be de-activated if the user
1068 // specifies a playlist name which already exists in the session.
1070 if (prompt) {
1072 ArdourPrompter prompter (true);
1074 prompter.set_title (_("New Copy Playlist"));
1075 prompter.set_prompt (_("Name for new playlist:"));
1076 prompter.set_initial_text (name);
1077 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1078 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1079 prompter.show_all ();
1081 switch (prompter.run ()) {
1082 case Gtk::RESPONSE_ACCEPT:
1083 prompter.get_result (name);
1084 break;
1086 default:
1087 return;
1091 if (name.length()) {
1092 tr->use_copy_playlist ();
1093 tr->playlist()->set_name (name);
1097 void
1098 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1100 string name;
1102 boost::shared_ptr<Track> tr = track ();
1103 if (!tr || tr->destructive()) {
1104 return;
1107 boost::shared_ptr<const Playlist> pl = tr->playlist();
1108 if (!pl) {
1109 return;
1112 name = pl->name();
1114 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1115 name = resolve_new_group_playlist_name(name,playlists_before_op);
1118 while (_session->playlists->by_name(name)) {
1119 name = Playlist::bump_name (name, *_session);
1123 if (prompt) {
1125 ArdourPrompter prompter (true);
1127 prompter.set_title (_("New Playlist"));
1128 prompter.set_prompt (_("Name for new playlist:"));
1129 prompter.set_initial_text (name);
1130 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1131 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1133 switch (prompter.run ()) {
1134 case Gtk::RESPONSE_ACCEPT:
1135 prompter.get_result (name);
1136 break;
1138 default:
1139 return;
1143 if (name.length()) {
1144 tr->use_new_playlist ();
1145 tr->playlist()->set_name (name);
1149 void
1150 RouteTimeAxisView::clear_playlist ()
1152 boost::shared_ptr<Track> tr = track ();
1153 if (!tr || tr->destructive()) {
1154 return;
1157 boost::shared_ptr<Playlist> pl = tr->playlist();
1158 if (!pl) {
1159 return;
1162 _editor.clear_playlist (pl);
1165 void
1166 RouteTimeAxisView::speed_changed ()
1168 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1171 void
1172 RouteTimeAxisView::update_diskstream_display ()
1174 if (!track()) {
1175 return;
1178 map_frozen ();
1181 void
1182 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1184 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1186 /* special case: select/deselect all tracks */
1187 if (_editor.get_selection().selected (this)) {
1188 _editor.get_selection().clear_tracks ();
1189 } else {
1190 _editor.select_all_tracks ();
1193 return;
1196 switch (ArdourKeyboard::selection_type (ev->state)) {
1197 case Selection::Toggle:
1198 _editor.get_selection().toggle (this);
1199 break;
1201 case Selection::Set:
1202 _editor.get_selection().set (this);
1203 break;
1205 case Selection::Extend:
1206 _editor.extend_selection_to_track (*this);
1207 break;
1209 case Selection::Add:
1210 _editor.get_selection().add (this);
1211 break;
1215 void
1216 RouteTimeAxisView::set_selected_points (PointSelection& points)
1218 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1219 (*i)->set_selected_points (points);
1223 void
1224 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1226 if (_view) {
1227 _view->set_selected_regionviews (regions);
1231 /** Add the selectable things that we have to a list.
1232 * @param results List to add things to.
1234 void
1235 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1237 double speed = 1.0;
1239 if (track() != 0) {
1240 speed = track()->speed();
1243 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1244 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1246 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1247 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1250 /* pick up visible automation tracks */
1252 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1253 if (!(*i)->hidden()) {
1254 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1259 void
1260 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1262 if (_view) {
1263 _view->get_inverted_selectables (sel, results);
1266 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1267 if (!(*i)->hidden()) {
1268 (*i)->get_inverted_selectables (sel, results);
1272 return;
1275 RouteGroup*
1276 RouteTimeAxisView::route_group () const
1278 return _route->route_group();
1281 string
1282 RouteTimeAxisView::name() const
1284 return _route->name();
1287 boost::shared_ptr<Playlist>
1288 RouteTimeAxisView::playlist () const
1290 boost::shared_ptr<Track> tr;
1292 if ((tr = track()) != 0) {
1293 return tr->playlist();
1294 } else {
1295 return boost::shared_ptr<Playlist> ();
1299 void
1300 RouteTimeAxisView::name_entry_changed ()
1302 string x;
1304 x = name_entry.get_text ();
1306 if (x == _route->name()) {
1307 return;
1310 strip_whitespace_edges(x);
1312 if (x.length() == 0) {
1313 name_entry.set_text (_route->name());
1314 return;
1317 if (!_session->route_name_unique (x)) {
1318 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1319 name_entry.set_text (_route->name());
1320 } else if (_session->route_name_internal (x)) {
1321 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1322 PROGRAM_NAME));
1323 name_entry.set_text (_route->name());
1324 } else {
1325 _route->set_name (x);
1329 boost::shared_ptr<Region>
1330 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1332 boost::shared_ptr<Playlist> pl = playlist ();
1334 if (pl) {
1335 return pl->find_next_region (pos, point, dir);
1338 return boost::shared_ptr<Region> ();
1341 framepos_t
1342 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1344 boost::shared_ptr<Playlist> pl = playlist ();
1346 if (pl) {
1347 return pl->find_next_region_boundary (pos, dir);
1350 return -1;
1353 void
1354 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1356 boost::shared_ptr<Playlist> what_we_got;
1357 boost::shared_ptr<Track> tr = track ();
1358 boost::shared_ptr<Playlist> playlist;
1360 if (tr == 0) {
1361 /* route is a bus, not a track */
1362 return;
1365 playlist = tr->playlist();
1367 TimeSelection time (selection.time);
1368 float const speed = tr->speed();
1369 if (speed != 1.0f) {
1370 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1371 (*i).start = session_frame_to_track_frame((*i).start, speed);
1372 (*i).end = session_frame_to_track_frame((*i).end, speed);
1376 playlist->clear_changes ();
1377 playlist->clear_owned_changes ();
1379 switch (op) {
1380 case Delete:
1381 if (playlist->cut (time) != 0) {
1382 vector<Command*> cmds;
1383 playlist->rdiff (cmds);
1384 _session->add_commands (cmds);
1386 _session->add_command (new StatefulDiffCommand (playlist));
1388 break;
1390 case Cut:
1391 if ((what_we_got = playlist->cut (time)) != 0) {
1392 _editor.get_cut_buffer().add (what_we_got);
1393 vector<Command*> cmds;
1394 playlist->rdiff (cmds);
1395 _session->add_commands (cmds);
1397 _session->add_command (new StatefulDiffCommand (playlist));
1399 break;
1400 case Copy:
1401 if ((what_we_got = playlist->copy (time)) != 0) {
1402 _editor.get_cut_buffer().add (what_we_got);
1404 break;
1406 case Clear:
1407 if ((what_we_got = playlist->cut (time)) != 0) {
1409 vector<Command*> cmds;
1410 playlist->rdiff (cmds);
1411 _session->add_commands (cmds);
1412 _session->add_command (new StatefulDiffCommand (playlist));
1413 what_we_got->release ();
1415 break;
1419 bool
1420 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1422 if (!is_track()) {
1423 return false;
1426 boost::shared_ptr<Playlist> pl = playlist ();
1427 PlaylistSelection::iterator p;
1429 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1431 if (p == selection.playlists.end()) {
1432 return false;
1435 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1437 if (track()->speed() != 1.0f) {
1438 pos = session_frame_to_track_frame (pos, track()->speed());
1439 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1442 pl->clear_changes ();
1443 pl->paste (*p, pos, times);
1444 _session->add_command (new StatefulDiffCommand (pl));
1446 return true;
1450 struct PlaylistSorter {
1451 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1452 return a->sort_id() < b->sort_id();
1456 void
1457 RouteTimeAxisView::build_playlist_menu ()
1459 using namespace Menu_Helpers;
1461 if (!is_track()) {
1462 return;
1465 delete playlist_action_menu;
1466 playlist_action_menu = new Menu;
1467 playlist_action_menu->set_name ("ArdourContextMenu");
1469 MenuList& playlist_items = playlist_action_menu->items();
1470 playlist_action_menu->set_name ("ArdourContextMenu");
1471 playlist_items.clear();
1473 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1474 boost::shared_ptr<Track> tr = track();
1475 RadioMenuItem::Group playlist_group;
1477 _session->playlists->get (playlists);
1479 /* find the playlists for this diskstream */
1480 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1481 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1482 playlists_tr.push_back(*i);
1486 /* sort the playlists */
1487 PlaylistSorter cmp;
1488 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1490 /* add the playlists to the menu */
1491 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1492 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1493 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1494 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1496 if (tr->playlist()->id() == (*i)->id()) {
1497 item->set_active();
1502 playlist_items.push_back (SeparatorElem());
1503 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1504 playlist_items.push_back (SeparatorElem());
1506 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1507 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1508 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1510 } else {
1511 // Use a label which tells the user what is happening
1512 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1513 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1517 playlist_items.push_back (SeparatorElem());
1518 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1519 playlist_items.push_back (SeparatorElem());
1521 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1524 void
1525 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1527 assert (is_track());
1529 // exit if we were triggered by deactivating the old playlist
1530 if (!item->get_active()) {
1531 return;
1534 boost::shared_ptr<Playlist> pl (wpl.lock());
1536 if (!pl) {
1537 return;
1540 if (track()->playlist() == pl) {
1541 // exit when use_playlist is called by the creation of the playlist menu
1542 // or the playlist choice is unchanged
1543 return;
1546 track()->use_playlist (pl);
1548 RouteGroup* rg = route_group();
1550 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1551 std::string group_string = "." + rg->name() + ".";
1553 std::string take_name = pl->name();
1554 std::string::size_type idx = take_name.find(group_string);
1556 if (idx == std::string::npos)
1557 return;
1559 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1561 boost::shared_ptr<RouteList> rl (rg->route_list());
1563 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1564 if ( (*i) == this->route()) {
1565 continue;
1568 std::string playlist_name = (*i)->name()+group_string+take_name;
1570 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1571 if (!track) {
1572 continue;
1575 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1576 if (!ipl) {
1577 // No playlist for this track for this take yet, make it
1578 track->use_new_playlist();
1579 track->playlist()->set_name(playlist_name);
1580 } else {
1581 track->use_playlist(ipl);
1587 void
1588 RouteTimeAxisView::show_playlist_selector ()
1590 _editor.playlist_selector().show_for (this);
1593 void
1594 RouteTimeAxisView::map_frozen ()
1596 if (!is_track()) {
1597 return;
1600 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1602 switch (track()->freeze_state()) {
1603 case Track::Frozen:
1604 playlist_button.set_sensitive (false);
1605 rec_enable_button->set_sensitive (false);
1606 break;
1607 default:
1608 playlist_button.set_sensitive (true);
1609 rec_enable_button->set_sensitive (true);
1610 break;
1614 void
1615 RouteTimeAxisView::color_handler ()
1617 //case cTimeStretchOutline:
1618 if (timestretch_rect) {
1619 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1621 //case cTimeStretchFill:
1622 if (timestretch_rect) {
1623 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1626 reset_meter();
1629 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1630 * Will add track if necessary.
1632 void
1633 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1635 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1636 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1638 if (!track) {
1639 /* it doesn't exist yet, so we don't care about the button state: just add it */
1640 create_automation_child (param, true);
1641 } else {
1642 assert (menu);
1643 bool yn = menu->get_active();
1644 bool changed = false;
1646 if ((changed = track->set_marked_for_display (menu->get_active())) && yn) {
1648 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1649 will have done that for us.
1652 if (changed && !no_redraw) {
1653 request_redraw ();
1659 void
1660 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1662 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1664 if (!track) {
1665 return;
1668 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1670 if (menu && !_hidden) {
1671 ignore_toggle = true;
1672 menu->set_active (false);
1673 ignore_toggle = false;
1676 if (_route && !no_redraw) {
1677 request_redraw ();
1682 void
1683 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1685 if (apply_to_selection) {
1686 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1687 } else {
1688 no_redraw = true;
1690 /* Show our automation */
1692 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1693 i->second->set_marked_for_display (true);
1695 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1697 if (menu) {
1698 menu->set_active(true);
1703 /* Show processor automation */
1705 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1706 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1707 if ((*ii)->view == 0) {
1708 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1711 (*ii)->menu_item->set_active (true);
1715 no_redraw = false;
1717 /* Redraw */
1719 request_redraw ();
1723 void
1724 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1726 if (apply_to_selection) {
1727 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1728 } else {
1729 no_redraw = true;
1731 /* Show our automation */
1733 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1734 if (i->second->has_automation()) {
1735 i->second->set_marked_for_display (true);
1737 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1738 if (menu) {
1739 menu->set_active(true);
1744 /* Show processor automation */
1746 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1747 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1748 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1749 (*ii)->menu_item->set_active (true);
1754 no_redraw = false;
1756 request_redraw ();
1760 void
1761 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1763 if (apply_to_selection) {
1764 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1765 } else {
1766 no_redraw = true;
1768 /* Hide our automation */
1770 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1771 i->second->set_marked_for_display (false);
1773 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1775 if (menu) {
1776 menu->set_active (false);
1780 /* Hide processor automation */
1782 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1783 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1784 (*ii)->menu_item->set_active (false);
1788 no_redraw = false;
1789 request_redraw ();
1794 void
1795 RouteTimeAxisView::region_view_added (RegionView* rv)
1797 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1798 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1799 boost::shared_ptr<AutomationTimeAxisView> atv;
1801 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1802 atv->add_ghost(rv);
1806 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1807 (*i)->add_ghost(rv);
1811 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1813 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1814 delete *i;
1819 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1821 parent.remove_processor_automation_node (this);
1824 void
1825 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1827 if (pan->view) {
1828 remove_child (pan->view);
1832 RouteTimeAxisView::ProcessorAutomationNode*
1833 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1835 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1837 if ((*i)->processor == processor) {
1839 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1840 if ((*ii)->what == what) {
1841 return *ii;
1847 return 0;
1850 /** Add an AutomationTimeAxisView to display automation for a processor's parameter */
1851 void
1852 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1854 string name;
1855 ProcessorAutomationNode* pan;
1857 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1858 /* session state may never have been saved with new plugin */
1859 error << _("programming error: ")
1860 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1861 processor->name(), what.type(), (int) what.channel(), what.id() )
1862 << endmsg;
1863 /*NOTREACHED*/
1864 return;
1867 if (pan->view) {
1868 return;
1871 boost::shared_ptr<AutomationControl> control
1872 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1874 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1875 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1876 _editor, *this, false, parent_canvas,
1877 processor->describe_parameter (what), processor->name()));
1879 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1881 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1883 if (_view) {
1884 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1888 void
1889 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1891 if (!_hidden) {
1892 pan->menu_item->set_active (false);
1895 if (!no_redraw) {
1896 request_redraw ();
1900 void
1901 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1903 boost::shared_ptr<Processor> processor (p.lock ());
1905 if (!processor || boost::dynamic_pointer_cast<Amp> (processor)) {
1906 /* The Amp processor is a special case and is dealt with separately */
1907 return;
1910 set<Evoral::Parameter> existing;
1912 processor->what_has_data (existing);
1914 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1916 Evoral::Parameter param (*i);
1917 boost::shared_ptr<AutomationLine> al;
1919 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1920 al->queue_reset ();
1921 } else {
1922 add_processor_automation_curve (processor, param);
1927 void
1928 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1930 using namespace Menu_Helpers;
1932 add_child (track);
1934 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1936 _automation_tracks[param] = track;
1938 /* existing state overrides "show" argument */
1939 string s = track->gui_property ("visible");
1940 if (!s.empty()) {
1941 show = string_is_affirmative (s);
1944 /* this might or might not change the visibility status, so don't rely on it */
1945 track->set_marked_for_display (show);
1947 if (show && !no_redraw) {
1948 request_redraw ();
1951 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1952 /* MIDI-related parameters are always in the menu, there's no
1953 reason to rebuild the menu just because we added a automation
1954 lane for one of them. But if we add a non-MIDI automation
1955 lane, then we need to invalidate the display menu.
1957 delete display_menu;
1958 display_menu = 0;
1962 void
1963 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1965 boost::shared_ptr<Processor> processor (p.lock ());
1967 if (!processor || !processor->display_to_user ()) {
1968 return;
1971 /* we use this override to veto the Amp processor from the plugin menu,
1972 as its automation lane can be accessed using the special "Fader" menu
1973 option
1976 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1977 return;
1980 using namespace Menu_Helpers;
1981 ProcessorAutomationInfo *rai;
1982 list<ProcessorAutomationInfo*>::iterator x;
1984 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1986 if (automatable.empty()) {
1987 return;
1990 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1991 if ((*x)->processor == processor) {
1992 break;
1996 if (x == processor_automation.end()) {
1998 rai = new ProcessorAutomationInfo (processor);
1999 processor_automation.push_back (rai);
2001 } else {
2003 rai = *x;
2007 /* any older menu was deleted at the top of processors_changed()
2008 when we cleared the subplugin menu.
2011 rai->menu = manage (new Menu);
2012 MenuList& items = rai->menu->items();
2013 rai->menu->set_name ("ArdourContextMenu");
2015 items.clear ();
2017 std::set<Evoral::Parameter> has_visible_automation;
2018 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2020 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2022 ProcessorAutomationNode* pan;
2023 CheckMenuItem* mitem;
2025 string name = processor->describe_parameter (*i);
2027 items.push_back (CheckMenuElem (name));
2028 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2030 _subplugin_menu_map[*i] = mitem;
2032 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2033 mitem->set_active(true);
2036 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2038 /* new item */
2040 pan = new ProcessorAutomationNode (*i, mitem, *this);
2042 rai->lines.push_back (pan);
2044 } else {
2046 pan->menu_item = mitem;
2050 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2053 /* add the menu for this processor, because the subplugin
2054 menu is always cleared at the top of processors_changed().
2055 this is the result of some poor design in gtkmm and/or
2056 GTK+.
2059 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2060 rai->valid = true;
2063 void
2064 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2065 RouteTimeAxisView::ProcessorAutomationNode* pan)
2067 bool showit = pan->menu_item->get_active();
2068 bool redraw = false;
2070 if (pan->view == 0 && showit) {
2071 add_processor_automation_curve (rai->processor, pan->what);
2072 redraw = true;
2075 if (pan->view && pan->view->set_marked_for_display (showit)) {
2076 redraw = true;
2079 if (redraw && !no_redraw) {
2080 request_redraw ();
2084 void
2085 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2087 if (c.type == RouteProcessorChange::MeterPointChange) {
2088 /* nothing to do if only the meter point has changed */
2089 return;
2092 using namespace Menu_Helpers;
2094 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2095 (*i)->valid = false;
2098 setup_processor_menu_and_curves ();
2100 bool deleted_processor_automation = false;
2102 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2104 list<ProcessorAutomationInfo*>::iterator tmp;
2106 tmp = i;
2107 ++tmp;
2109 if (!(*i)->valid) {
2111 delete *i;
2112 processor_automation.erase (i);
2113 deleted_processor_automation = true;
2117 i = tmp;
2120 if (deleted_processor_automation && !no_redraw) {
2121 request_redraw ();
2125 boost::shared_ptr<AutomationLine>
2126 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2128 ProcessorAutomationNode* pan;
2130 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2131 if (pan->view) {
2132 pan->view->line();
2136 return boost::shared_ptr<AutomationLine>();
2139 void
2140 RouteTimeAxisView::reset_processor_automation_curves ()
2142 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2143 (*i)->reset();
2147 void
2148 RouteTimeAxisView::update_rec_display ()
2150 RouteUI::update_rec_display ();
2151 name_entry.set_sensitive (!_route->record_enabled());
2154 void
2155 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2157 if (apply_to_selection) {
2158 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2159 } else {
2161 if (_view) {
2162 _view->set_layer_display (d);
2165 set_gui_property (X_("layer-display"), enum_2_string (d));
2169 LayerDisplay
2170 RouteTimeAxisView::layer_display () const
2172 if (_view) {
2173 return _view->layer_display ();
2176 /* we don't know, since we don't have a _view, so just return something */
2177 return Overlaid;
2182 boost::shared_ptr<AutomationTimeAxisView>
2183 RouteTimeAxisView::automation_child(Evoral::Parameter param)
2185 AutomationTracks::iterator i = _automation_tracks.find(param);
2186 if (i != _automation_tracks.end()) {
2187 return i->second;
2188 } else {
2189 return boost::shared_ptr<AutomationTimeAxisView>();
2193 void
2194 RouteTimeAxisView::fast_update ()
2196 gm.get_level_meter().update_meters ();
2199 void
2200 RouteTimeAxisView::hide_meter ()
2202 clear_meter ();
2203 gm.get_level_meter().hide_meters ();
2206 void
2207 RouteTimeAxisView::show_meter ()
2209 reset_meter ();
2212 void
2213 RouteTimeAxisView::reset_meter ()
2215 if (Config->get_show_track_meters()) {
2216 gm.get_level_meter().setup_meters (height-5);
2217 } else {
2218 hide_meter ();
2222 void
2223 RouteTimeAxisView::clear_meter ()
2225 gm.get_level_meter().clear_meters ();
2228 void
2229 RouteTimeAxisView::meter_changed ()
2231 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::meter_changed)
2232 reset_meter();
2235 void
2236 RouteTimeAxisView::io_changed (IOChange /*change*/, void */*src*/)
2238 reset_meter ();
2241 void
2242 RouteTimeAxisView::build_underlay_menu(Gtk::Menu* parent_menu)
2244 using namespace Menu_Helpers;
2246 if (!_underlay_streams.empty()) {
2247 MenuList& parent_items = parent_menu->items();
2248 Menu* gs_menu = manage (new Menu);
2249 gs_menu->set_name ("ArdourContextMenu");
2250 MenuList& gs_items = gs_menu->items();
2252 parent_items.push_back (MenuElem (_("Underlays"), *gs_menu));
2254 for(UnderlayList::iterator it = _underlay_streams.begin(); it != _underlay_streams.end(); ++it) {
2255 gs_items.push_back(MenuElem(string_compose(_("Remove \"%1\""), (*it)->trackview().name()),
2256 sigc::bind(sigc::mem_fun(*this, &RouteTimeAxisView::remove_underlay), *it)));
2261 bool
2262 RouteTimeAxisView::set_underlay_state()
2264 if (!underlay_xml_node) {
2265 return false;
2268 XMLNodeList nlist = underlay_xml_node->children();
2269 XMLNodeConstIterator niter;
2270 XMLNode *child_node;
2272 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2273 child_node = *niter;
2275 if (child_node->name() != "Underlay") {
2276 continue;
2279 XMLProperty* prop = child_node->property ("id");
2280 if (prop) {
2281 PBD::ID id (prop->value());
2283 RouteTimeAxisView* v = _editor.get_route_view_by_route_id (id);
2285 if (v) {
2286 add_underlay(v->view(), false);
2291 return false;
2294 void
2295 RouteTimeAxisView::add_underlay (StreamView* v, bool update_xml)
2297 if (!v) {
2298 return;
2301 RouteTimeAxisView& other = v->trackview();
2303 if (find(_underlay_streams.begin(), _underlay_streams.end(), v) == _underlay_streams.end()) {
2304 if (find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this) != other._underlay_mirrors.end()) {
2305 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2306 /*NOTREACHED*/
2309 _underlay_streams.push_back(v);
2310 other._underlay_mirrors.push_back(this);
2312 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::add_ghost));
2314 #ifdef GUI_OBJECT_STATE_FIX_REQUIRED
2315 if (update_xml) {
2316 if (!underlay_xml_node) {
2317 underlay_xml_node = xml_node->add_child("Underlays");
2320 XMLNode* node = underlay_xml_node->add_child("Underlay");
2321 XMLProperty* prop = node->add_property("id");
2322 prop->set_value(v->trackview().route()->id().to_s());
2324 #endif
2328 void
2329 RouteTimeAxisView::remove_underlay (StreamView* v)
2331 if (!v) {
2332 return;
2335 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2336 RouteTimeAxisView& other = v->trackview();
2338 if (it != _underlay_streams.end()) {
2339 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2341 if (gm == other._underlay_mirrors.end()) {
2342 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2343 /*NOTREACHED*/
2346 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2348 _underlay_streams.erase(it);
2349 other._underlay_mirrors.erase(gm);
2351 if (underlay_xml_node) {
2352 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2357 void
2358 RouteTimeAxisView::set_button_names ()
2360 rec_enable_button_label.set_text (_("r"));
2362 if (_route && _route->solo_safe()) {
2363 solo_button_label.set_text (X_("!"));
2364 } else {
2365 if (Config->get_solo_control_is_listen_control()) {
2366 switch (Config->get_listen_position()) {
2367 case AfterFaderListen:
2368 solo_button_label.set_text (_("A"));
2369 break;
2370 case PreFaderListen:
2371 solo_button_label.set_text (_("P"));
2372 break;
2374 } else {
2375 solo_button_label.set_text (_("s"));
2378 mute_button_label.set_text (_("m"));
2381 Gtk::CheckMenuItem*
2382 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2384 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2385 if (i != _main_automation_menu_map.end()) {
2386 return i->second;
2389 i = _subplugin_menu_map.find (param);
2390 if (i != _subplugin_menu_map.end()) {
2391 return i->second;
2394 return 0;
2397 void
2398 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2400 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2401 if (!c) {
2402 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2403 return;
2406 gain_track.reset (new AutomationTimeAxisView (_session,
2407 _route, _route->amp(), c, param,
2408 _editor,
2409 *this,
2410 false,
2411 parent_canvas,
2412 _route->amp()->describe_parameter(param)));
2414 if (_view) {
2415 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2418 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2421 static
2422 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2424 l->push_back (rv->region());
2427 RegionView*
2428 RouteTimeAxisView::combine_regions ()
2430 /* as of may 2011, we do not offer uncombine for MIDI tracks
2433 if (!is_audio_track()) {
2434 return 0;
2437 if (!_view) {
2438 return 0;
2441 Playlist::RegionList selected_regions;
2442 boost::shared_ptr<Playlist> playlist = track()->playlist();
2444 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2446 if (selected_regions.size() < 2) {
2447 return 0;
2450 playlist->clear_changes ();
2451 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2453 _session->add_command (new StatefulDiffCommand (playlist));
2454 /* make the new region be selected */
2456 return _view->find_view (compound_region);
2459 void
2460 RouteTimeAxisView::uncombine_regions ()
2462 /* as of may 2011, we do not offer uncombine for MIDI tracks
2464 if (!is_audio_track()) {
2465 return;
2468 if (!_view) {
2469 return;
2472 Playlist::RegionList selected_regions;
2473 boost::shared_ptr<Playlist> playlist = track()->playlist();
2475 /* have to grab selected regions first because the uncombine is going
2476 * to change that in the middle of the list traverse
2479 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2481 playlist->clear_changes ();
2483 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2484 playlist->uncombine (*i);
2487 _session->add_command (new StatefulDiffCommand (playlist));
2490 string
2491 RouteTimeAxisView::state_id() const
2493 return string_compose ("rtav %1", _route->id().to_s());