increase threshold for drag-playhead-does-vertical-zoom
[ardour2.git] / gtk2_ardour / route_time_axis.cc
blob7f9575f015e11267380b96130c59bff705a02c39
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 return 0;
365 void
366 RouteTimeAxisView::build_automation_action_menu (bool for_selection)
368 using namespace Menu_Helpers;
370 /* detach subplugin_menu from automation_action_menu before we delete automation_action_menu,
371 otherwise bad things happen (see comment for similar case in MidiTimeAxisView::build_automation_action_menu)
374 detach_menu (subplugin_menu);
376 _main_automation_menu_map.clear ();
377 delete automation_action_menu;
378 automation_action_menu = new Menu;
380 MenuList& items = automation_action_menu->items();
382 automation_action_menu->set_name ("ArdourContextMenu");
384 items.push_back (MenuElem (_("Show All Automation"),
385 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_all_automation), for_selection)));
387 items.push_back (MenuElem (_("Show Existing Automation"),
388 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::show_existing_automation), for_selection)));
390 items.push_back (MenuElem (_("Hide All Automation"),
391 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::hide_all_automation), for_selection)));
393 items.push_back (SeparatorElem ());
395 /* Attach the plugin submenu. It may have previously been used elsewhere,
396 so it was detached above */
398 items.push_back (MenuElem (_("Plugins"), subplugin_menu));
399 items.back().set_sensitive (!subplugin_menu.items().empty() && (!for_selection || _editor.get_selection().tracks.size() == 1));;
402 void
403 RouteTimeAxisView::build_display_menu ()
405 using namespace Menu_Helpers;
407 /* prepare it */
409 TimeAxisView::build_display_menu ();
411 /* now fill it with our stuff */
413 MenuList& items = display_menu->items();
414 display_menu->set_name ("ArdourContextMenu");
416 items.push_back (MenuElem (_("Color..."), sigc::mem_fun (*this, &RouteUI::choose_color)));
418 if (_size_menu) {
419 detach_menu (*_size_menu);
421 build_size_menu ();
422 items.push_back (MenuElem (_("Height"), *_size_menu));
424 items.push_back (SeparatorElem());
426 if (!Profile->get_sae()) {
427 items.push_back (MenuElem (_("Remote Control ID..."), sigc::mem_fun (*this, &RouteUI::open_remote_control_id_dialog)));
428 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
429 items.push_back (SeparatorElem());
432 // Hook for derived classes to add type specific stuff
433 append_extra_display_menu_items ();
435 if (is_track()) {
437 Menu* layers_menu = manage (new Menu);
438 MenuList &layers_items = layers_menu->items();
439 layers_menu->set_name("ArdourContextMenu");
441 RadioMenuItem::Group layers_group;
443 /* Find out how many overlaid/stacked tracks we have in the selection */
445 int overlaid = 0;
446 int stacked = 0;
447 TrackSelection const & s = _editor.get_selection().tracks;
448 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
449 StreamView* v = (*i)->view ();
450 if (!v) {
451 continue;
454 switch (v->layer_display ()) {
455 case Overlaid:
456 ++overlaid;
457 break;
458 case Stacked:
459 ++stacked;
460 break;
464 /* We're not connecting to signal_toggled() here; in the case where these two items are
465 set to be in the `inconsistent' state, it seems that one or other will end up active
466 as well as inconsistent (presumably due to the RadioMenuItem::Group). Then when you
467 select the active one, no toggled signal is emitted so nothing happens.
470 layers_items.push_back (RadioMenuElem (layers_group, _("Overlaid")));
471 RadioMenuItem* i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
472 i->set_active (overlaid != 0 && stacked == 0);
473 i->set_inconsistent (overlaid != 0 && stacked != 0);
474 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Overlaid, true));
476 layers_items.push_back (
477 RadioMenuElem (layers_group, _("Stacked"),
478 sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true))
481 i = dynamic_cast<RadioMenuItem*> (&layers_items.back ());
482 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_layer_display), Stacked, true));
483 i->set_active (overlaid == 0 && stacked != 0);
484 i->set_inconsistent (overlaid != 0 && stacked != 0);
486 items.push_back (MenuElem (_("Layers"), *layers_menu));
488 if (!Profile->get_sae()) {
490 Menu* alignment_menu = manage (new Menu);
491 MenuList& alignment_items = alignment_menu->items();
492 alignment_menu->set_name ("ArdourContextMenu");
494 RadioMenuItem::Group align_group;
496 /* Same verbose hacks as for the layering options above */
498 int existing = 0;
499 int capture = 0;
500 int automatic = 0;
501 int styles = 0;
502 boost::shared_ptr<Track> first_track;
504 TrackSelection const & s = _editor.get_selection().tracks;
505 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
506 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
507 if (!r || !r->is_track ()) {
508 continue;
511 if (!first_track) {
512 first_track = r->track();
515 switch (r->track()->alignment_choice()) {
516 case Automatic:
517 ++automatic;
518 styles |= 0x1;
519 switch (r->track()->alignment_style()) {
520 case ExistingMaterial:
521 ++existing;
522 break;
523 case CaptureTime:
524 ++capture;
525 break;
527 break;
528 case UseExistingMaterial:
529 ++existing;
530 styles |= 0x2;
531 break;
532 case UseCaptureTime:
533 ++capture;
534 styles |= 0x4;
535 break;
539 bool inconsistent;
540 switch (styles) {
541 case 1:
542 case 2:
543 case 4:
544 inconsistent = false;
545 break;
546 default:
547 inconsistent = true;
548 break;
551 RadioMenuItem* i;
553 if (!inconsistent && first_track) {
555 alignment_items.push_back (RadioMenuElem (align_group, _("Automatic (based on I/O connections)")));
556 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
557 i->set_active (automatic != 0 && existing == 0 && capture == 0);
558 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, Automatic, true));
560 switch (first_track->alignment_choice()) {
561 case Automatic:
562 switch (first_track->alignment_style()) {
563 case ExistingMaterial:
564 alignment_items.push_back (MenuElem (_("(Currently: Existing Material)")));
565 break;
566 case CaptureTime:
567 alignment_items.push_back (MenuElem (_("(Currently: Capture Time)")));
568 break;
570 break;
571 default:
572 break;
575 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Existing Material")));
576 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
577 i->set_active (existing != 0 && capture == 0 && automatic == 0);
578 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseExistingMaterial, true));
580 alignment_items.push_back (RadioMenuElem (align_group, _("Align With Capture Time")));
581 i = dynamic_cast<RadioMenuItem*> (&alignment_items.back());
582 i->set_active (existing == 0 && capture != 0 && automatic == 0);
583 i->signal_activate().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::set_align_choice), i, UseCaptureTime, true));
585 items.push_back (MenuElem (_("Alignment"), *alignment_menu));
587 } else {
588 /* show nothing */
591 Menu* mode_menu = manage (new Menu);
592 MenuList& mode_items = mode_menu->items ();
593 mode_menu->set_name ("ArdourContextMenu");
595 RadioMenuItem::Group mode_group;
597 int normal = 0;
598 int tape = 0;
599 int non_layered = 0;
601 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
602 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
603 if (!r || !r->is_track ()) {
604 continue;
607 switch (r->track()->mode()) {
608 case Normal:
609 ++normal;
610 break;
611 case Destructive:
612 ++tape;
613 break;
614 case NonLayered:
615 ++non_layered;
616 break;
620 mode_items.push_back (RadioMenuElem (mode_group, _("Normal Mode")));
621 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
622 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Normal, true));
623 i->set_active (normal != 0 && tape == 0 && non_layered == 0);
624 i->set_inconsistent (normal != 0 && (tape != 0 || non_layered != 0));
626 mode_items.push_back (RadioMenuElem (mode_group, _("Tape Mode")));
627 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
628 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::Destructive, true));
629 i->set_active (normal == 0 && tape != 0 && non_layered == 0);
630 i->set_inconsistent (tape != 0 && (normal != 0 || non_layered != 0));
632 mode_items.push_back (RadioMenuElem (mode_group, _("Non-Layered Mode")));
633 i = dynamic_cast<RadioMenuItem*> (&mode_items.back ());
634 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::set_track_mode), ARDOUR::NonLayered, true));
635 i->set_active (normal == 0 && tape == 0 && non_layered != 0);
636 i->set_inconsistent (non_layered != 0 && (normal != 0 || tape != 0));
638 items.push_back (MenuElem (_("Mode"), *mode_menu));
641 color_mode_menu = build_color_mode_menu();
642 if (color_mode_menu) {
643 items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
646 items.push_back (SeparatorElem());
648 build_playlist_menu ();
649 items.push_back (MenuElem (_("Playlist"), *playlist_action_menu));
650 items.back().set_sensitive (_editor.get_selection().tracks.size() <= 1);
652 route_group_menu->detach ();
654 WeakRouteList r;
655 for (TrackSelection::iterator i = _editor.get_selection().tracks.begin(); i != _editor.get_selection().tracks.end(); ++i) {
656 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
657 if (rtv) {
658 r.push_back (rtv->route ());
662 if (r.empty ()) {
663 r.push_back (route ());
666 route_group_menu->build (r);
667 items.push_back (MenuElem (_("Route Group"), *route_group_menu->menu ()));
669 build_automation_action_menu (true);
670 items.push_back (MenuElem (_("Automation"), *automation_action_menu));
672 items.push_back (SeparatorElem());
675 int active = 0;
676 int inactive = 0;
677 TrackSelection const & s = _editor.get_selection().tracks;
678 for (TrackSelection::const_iterator i = s.begin(); i != s.end(); ++i) {
679 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
680 if (!r) {
681 continue;
684 if (r->route()->active()) {
685 ++active;
686 } else {
687 ++inactive;
691 items.push_back (CheckMenuElem (_("Active")));
692 CheckMenuItem* i = dynamic_cast<CheckMenuItem *> (&items.back());
693 bool click_sets_active = true;
694 if (active > 0 && inactive == 0) {
695 i->set_active (true);
696 click_sets_active = false;
697 } else if (active > 0 && inactive > 0) {
698 i->set_inconsistent (true);
700 i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
702 items.push_back (SeparatorElem());
703 items.push_back (MenuElem (_("Hide"), sigc::bind (sigc::mem_fun(_editor, &PublicEditor::hide_track_in_display), this, true)));
704 if (!Profile->get_sae()) {
705 items.push_back (MenuElem (_("Remove"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
706 } else {
707 items.push_front (SeparatorElem());
708 items.push_front (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun(*this, &RouteUI::remove_this_route), true)));
712 void
713 RouteTimeAxisView::set_track_mode (TrackMode mode, bool apply_to_selection)
715 if (apply_to_selection) {
716 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_track_mode, _1, mode, false));
717 } else {
719 bool needs_bounce;
721 if (!track()->can_use_mode (mode, needs_bounce)) {
723 if (!needs_bounce) {
724 /* cannot be done */
725 return;
726 } else {
727 cerr << "would bounce this one\n";
728 return;
732 track()->set_mode (mode);
734 rec_enable_button->remove ();
736 switch (mode) {
737 case ARDOUR::NonLayered:
738 case ARDOUR::Normal:
739 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_normal_red"))))));
740 break;
741 case ARDOUR::Destructive:
742 rec_enable_button->add (*(manage (new Image (::get_icon (X_("record_tape_red"))))));
743 break;
746 rec_enable_button->show_all ();
750 void
751 RouteTimeAxisView::show_timestretch (framepos_t start, framepos_t end)
753 double x1;
754 double x2;
755 double y2;
757 TimeAxisView::show_timestretch (start, end);
759 hide_timestretch ();
761 #if 0
762 if (ts.empty()) {
763 return;
767 /* check that the time selection was made in our route, or our route group.
768 remember that route_group() == 0 implies the route is *not* in a edit group.
771 if (!(ts.track == this || (ts.group != 0 && ts.group == _route->route_group()))) {
772 /* this doesn't apply to us */
773 return;
776 /* ignore it if our edit group is not active */
778 if ((ts.track != this) && _route->route_group() && !_route->route_group()->is_active()) {
779 return;
781 #endif
783 if (timestretch_rect == 0) {
784 timestretch_rect = new SimpleRect (*canvas_display ());
785 timestretch_rect->property_x1() = 0.0;
786 timestretch_rect->property_y1() = 0.0;
787 timestretch_rect->property_x2() = 0.0;
788 timestretch_rect->property_y2() = 0.0;
789 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
790 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
793 timestretch_rect->show ();
794 timestretch_rect->raise_to_top ();
796 x1 = start / _editor.get_current_zoom();
797 x2 = (end - 1) / _editor.get_current_zoom();
798 y2 = current_height() - 2;
800 timestretch_rect->property_x1() = x1;
801 timestretch_rect->property_y1() = 1.0;
802 timestretch_rect->property_x2() = x2;
803 timestretch_rect->property_y2() = y2;
806 void
807 RouteTimeAxisView::hide_timestretch ()
809 TimeAxisView::hide_timestretch ();
811 if (timestretch_rect) {
812 timestretch_rect->hide ();
816 void
817 RouteTimeAxisView::show_selection (TimeSelection& ts)
820 #if 0
821 /* ignore it if our edit group is not active or if the selection was started
822 in some other track or route group (remember that route_group() == 0 means
823 that the track is not in an route group).
826 if (((ts.track != this && !is_child (ts.track)) && _route->route_group() && !_route->route_group()->is_active()) ||
827 (!(ts.track == this || is_child (ts.track) || (ts.group != 0 && ts.group == _route->route_group())))) {
828 hide_selection ();
829 return;
831 #endif
833 TimeAxisView::show_selection (ts);
836 void
837 RouteTimeAxisView::set_height (uint32_t h)
839 int gmlen = h - 5;
840 bool height_changed = (height == 0) || (h != height);
841 gm.get_level_meter().setup_meters (gmlen);
843 TimeAxisView::set_height (h);
845 ensure_xml_node ();
847 if (_view) {
848 _view->set_height ((double) current_height());
851 char buf[32];
852 snprintf (buf, sizeof (buf), "%u", height);
853 xml_node->add_property ("height", buf);
855 if (height >= preset_height (HeightNormal)) {
857 reset_meter();
859 gm.get_gain_slider().show();
860 mute_button->show();
861 if (!_route || _route->is_monitor()) {
862 solo_button->hide();
863 } else {
864 solo_button->show();
866 if (rec_enable_button)
867 rec_enable_button->show();
869 route_group_button.show();
870 automation_button.show();
872 if (is_track() && track()->mode() == ARDOUR::Normal) {
873 playlist_button.show();
876 } else {
878 reset_meter();
880 gm.get_gain_slider().hide();
881 mute_button->show();
882 if (!_route || _route->is_monitor()) {
883 solo_button->hide();
884 } else {
885 solo_button->show();
887 if (rec_enable_button)
888 rec_enable_button->show();
890 route_group_button.hide ();
891 automation_button.hide ();
893 if (is_track() && track()->mode() == ARDOUR::Normal) {
894 playlist_button.hide ();
899 if (height_changed && !no_redraw) {
900 /* only emit the signal if the height really changed */
901 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
905 void
906 RouteTimeAxisView::set_color (Gdk::Color const & c)
908 RouteUI::set_color (c);
910 if (_view) {
911 _view->apply_color (_color, StreamView::RegionColor);
915 void
916 RouteTimeAxisView::reset_samples_per_unit ()
918 set_samples_per_unit (_editor.get_current_zoom());
921 void
922 RouteTimeAxisView::horizontal_position_changed ()
924 if (_view) {
925 _view->horizontal_position_changed ();
929 void
930 RouteTimeAxisView::set_samples_per_unit (double spu)
932 double speed = 1.0;
934 if (track()) {
935 speed = track()->speed();
938 if (_view) {
939 _view->set_samples_per_unit (spu * speed);
942 TimeAxisView::set_samples_per_unit (spu * speed);
945 void
946 RouteTimeAxisView::set_align_choice (RadioMenuItem* mitem, AlignChoice choice, bool apply_to_selection)
948 if (!mitem->get_active()) {
949 /* this is one of the two calls made when these radio menu items change status. this one
950 is for the item that became inactive, and we want to ignore it.
952 return;
955 if (apply_to_selection) {
956 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_align_choice, _1, mitem, choice, false));
957 } else {
958 if (track ()) {
959 track()->set_align_choice (choice);
964 void
965 RouteTimeAxisView::rename_current_playlist ()
967 ArdourPrompter prompter (true);
968 string name;
970 boost::shared_ptr<Track> tr = track();
971 if (!tr || tr->destructive()) {
972 return;
975 boost::shared_ptr<Playlist> pl = tr->playlist();
976 if (!pl) {
977 return;
980 prompter.set_title (_("Rename Playlist"));
981 prompter.set_prompt (_("New name for playlist:"));
982 prompter.set_initial_text (pl->name());
983 prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
984 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
986 switch (prompter.run ()) {
987 case Gtk::RESPONSE_ACCEPT:
988 prompter.get_result (name);
989 if (name.length()) {
990 pl->set_name (name);
992 break;
994 default:
995 break;
999 std::string
1000 RouteTimeAxisView::resolve_new_group_playlist_name(std::string &basename, vector<boost::shared_ptr<Playlist> > const & playlists)
1002 std::string ret (basename);
1004 std::string const group_string = "." + route_group()->name() + ".";
1006 // iterate through all playlists
1007 int maxnumber = 0;
1008 for (vector<boost::shared_ptr<Playlist> >::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1009 std::string tmp = (*i)->name();
1011 std::string::size_type idx = tmp.find(group_string);
1012 // find those which belong to this group
1013 if (idx != string::npos) {
1014 tmp = tmp.substr(idx + group_string.length());
1016 // and find the largest current number
1017 int x = atoi(tmp.c_str());
1018 if (x > maxnumber) {
1019 maxnumber = x;
1024 maxnumber++;
1026 char buf[32];
1027 snprintf (buf, sizeof(buf), "%d", maxnumber);
1029 ret = this->name() + "." + route_group()->name () + "." + buf;
1031 return ret;
1034 void
1035 RouteTimeAxisView::use_copy_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1037 string name;
1039 boost::shared_ptr<Track> tr = track ();
1040 if (!tr || tr->destructive()) {
1041 return;
1044 boost::shared_ptr<const Playlist> pl = tr->playlist();
1045 if (!pl) {
1046 return;
1049 name = pl->name();
1051 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1052 name = resolve_new_group_playlist_name(name, playlists_before_op);
1055 while (_session->playlists->by_name(name)) {
1056 name = Playlist::bump_name (name, *_session);
1059 // TODO: The prompter "new" button should be de-activated if the user
1060 // specifies a playlist name which already exists in the session.
1062 if (prompt) {
1064 ArdourPrompter prompter (true);
1066 prompter.set_title (_("New Copy Playlist"));
1067 prompter.set_prompt (_("Name for new playlist:"));
1068 prompter.set_initial_text (name);
1069 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1070 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1071 prompter.show_all ();
1073 switch (prompter.run ()) {
1074 case Gtk::RESPONSE_ACCEPT:
1075 prompter.get_result (name);
1076 break;
1078 default:
1079 return;
1083 if (name.length()) {
1084 tr->use_copy_playlist ();
1085 tr->playlist()->set_name (name);
1089 void
1090 RouteTimeAxisView::use_new_playlist (bool prompt, vector<boost::shared_ptr<Playlist> > const & playlists_before_op)
1092 string name;
1094 boost::shared_ptr<Track> tr = track ();
1095 if (!tr || tr->destructive()) {
1096 return;
1099 boost::shared_ptr<const Playlist> pl = tr->playlist();
1100 if (!pl) {
1101 return;
1104 name = pl->name();
1106 if (route_group() && route_group()->is_active() && route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1107 name = resolve_new_group_playlist_name(name,playlists_before_op);
1110 while (_session->playlists->by_name(name)) {
1111 name = Playlist::bump_name (name, *_session);
1115 if (prompt) {
1117 ArdourPrompter prompter (true);
1119 prompter.set_title (_("New Playlist"));
1120 prompter.set_prompt (_("Name for new playlist:"));
1121 prompter.set_initial_text (name);
1122 prompter.add_button (Gtk::Stock::NEW, Gtk::RESPONSE_ACCEPT);
1123 prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
1125 switch (prompter.run ()) {
1126 case Gtk::RESPONSE_ACCEPT:
1127 prompter.get_result (name);
1128 break;
1130 default:
1131 return;
1135 if (name.length()) {
1136 tr->use_new_playlist ();
1137 tr->playlist()->set_name (name);
1141 void
1142 RouteTimeAxisView::clear_playlist ()
1144 boost::shared_ptr<Track> tr = track ();
1145 if (!tr || tr->destructive()) {
1146 return;
1149 boost::shared_ptr<Playlist> pl = tr->playlist();
1150 if (!pl) {
1151 return;
1154 _editor.clear_playlist (pl);
1157 void
1158 RouteTimeAxisView::speed_changed ()
1160 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&RouteTimeAxisView::reset_samples_per_unit, this));
1163 void
1164 RouteTimeAxisView::update_diskstream_display ()
1166 if (!track()) {
1167 return;
1170 map_frozen ();
1173 void
1174 RouteTimeAxisView::selection_click (GdkEventButton* ev)
1176 if (Keyboard::modifier_state_equals (ev->state, (Keyboard::TertiaryModifier|Keyboard::PrimaryModifier))) {
1178 /* special case: select/deselect all tracks */
1179 if (_editor.get_selection().selected (this)) {
1180 _editor.get_selection().clear_tracks ();
1181 } else {
1182 _editor.select_all_tracks ();
1185 return;
1188 switch (ArdourKeyboard::selection_type (ev->state)) {
1189 case Selection::Toggle:
1190 _editor.get_selection().toggle (this);
1191 break;
1193 case Selection::Set:
1194 _editor.get_selection().set (this);
1195 break;
1197 case Selection::Extend:
1198 _editor.extend_selection_to_track (*this);
1199 break;
1201 case Selection::Add:
1202 _editor.get_selection().add (this);
1203 break;
1207 void
1208 RouteTimeAxisView::set_selected_points (PointSelection& points)
1210 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1211 (*i)->set_selected_points (points);
1215 void
1216 RouteTimeAxisView::set_selected_regionviews (RegionSelection& regions)
1218 if (_view) {
1219 _view->set_selected_regionviews (regions);
1223 /** Add the selectable things that we have to a list.
1224 * @param results List to add things to.
1226 void
1227 RouteTimeAxisView::get_selectables (framepos_t start, framepos_t end, double top, double bot, list<Selectable*>& results)
1229 double speed = 1.0;
1231 if (track() != 0) {
1232 speed = track()->speed();
1235 framepos_t const start_adjusted = session_frame_to_track_frame(start, speed);
1236 framepos_t const end_adjusted = session_frame_to_track_frame(end, speed);
1238 if ((_view && ((top < 0.0 && bot < 0.0))) || touched (top, bot)) {
1239 _view->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1242 /* pick up visible automation tracks */
1244 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1245 if (!(*i)->hidden()) {
1246 (*i)->get_selectables (start_adjusted, end_adjusted, top, bot, results);
1251 void
1252 RouteTimeAxisView::get_inverted_selectables (Selection& sel, list<Selectable*>& results)
1254 if (_view) {
1255 _view->get_inverted_selectables (sel, results);
1258 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1259 if (!(*i)->hidden()) {
1260 (*i)->get_inverted_selectables (sel, results);
1264 return;
1267 RouteGroup*
1268 RouteTimeAxisView::route_group () const
1270 return _route->route_group();
1273 string
1274 RouteTimeAxisView::name() const
1276 return _route->name();
1279 boost::shared_ptr<Playlist>
1280 RouteTimeAxisView::playlist () const
1282 boost::shared_ptr<Track> tr;
1284 if ((tr = track()) != 0) {
1285 return tr->playlist();
1286 } else {
1287 return boost::shared_ptr<Playlist> ();
1291 void
1292 RouteTimeAxisView::name_entry_changed ()
1294 string x;
1296 x = name_entry.get_text ();
1298 if (x == _route->name()) {
1299 return;
1302 strip_whitespace_edges(x);
1304 if (x.length() == 0) {
1305 name_entry.set_text (_route->name());
1306 return;
1309 if (!_session->route_name_unique (x)) {
1310 ARDOUR_UI::instance()->popup_error (_("A track already exists with that name"));
1311 name_entry.set_text (_route->name());
1312 } else if (_session->route_name_internal (x)) {
1313 ARDOUR_UI::instance()->popup_error (string_compose (_("You cannot create a track with that name as it is reserved for %1"),
1314 PROGRAM_NAME));
1315 name_entry.set_text (_route->name());
1316 } else {
1317 _route->set_name (x);
1321 boost::shared_ptr<Region>
1322 RouteTimeAxisView::find_next_region (framepos_t pos, RegionPoint point, int32_t dir)
1324 boost::shared_ptr<Playlist> pl = playlist ();
1326 if (pl) {
1327 return pl->find_next_region (pos, point, dir);
1330 return boost::shared_ptr<Region> ();
1333 framepos_t
1334 RouteTimeAxisView::find_next_region_boundary (framepos_t pos, int32_t dir)
1336 boost::shared_ptr<Playlist> pl = playlist ();
1338 if (pl) {
1339 return pl->find_next_region_boundary (pos, dir);
1342 return -1;
1345 void
1346 RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
1348 boost::shared_ptr<Playlist> what_we_got;
1349 boost::shared_ptr<Track> tr = track ();
1350 boost::shared_ptr<Playlist> playlist;
1352 if (tr == 0) {
1353 /* route is a bus, not a track */
1354 return;
1357 playlist = tr->playlist();
1359 TimeSelection time (selection.time);
1360 float const speed = tr->speed();
1361 if (speed != 1.0f) {
1362 for (TimeSelection::iterator i = time.begin(); i != time.end(); ++i) {
1363 (*i).start = session_frame_to_track_frame((*i).start, speed);
1364 (*i).end = session_frame_to_track_frame((*i).end, speed);
1368 playlist->clear_changes ();
1369 playlist->clear_owned_changes ();
1371 switch (op) {
1372 case Delete:
1373 if (playlist->cut (time) != 0) {
1374 vector<Command*> cmds;
1375 playlist->rdiff (cmds);
1376 _session->add_commands (cmds);
1378 _session->add_command (new StatefulDiffCommand (playlist));
1380 break;
1382 case Cut:
1383 if ((what_we_got = playlist->cut (time)) != 0) {
1384 _editor.get_cut_buffer().add (what_we_got);
1385 vector<Command*> cmds;
1386 playlist->rdiff (cmds);
1387 _session->add_commands (cmds);
1389 _session->add_command (new StatefulDiffCommand (playlist));
1391 break;
1392 case Copy:
1393 if ((what_we_got = playlist->copy (time)) != 0) {
1394 _editor.get_cut_buffer().add (what_we_got);
1396 break;
1398 case Clear:
1399 if ((what_we_got = playlist->cut (time)) != 0) {
1401 vector<Command*> cmds;
1402 playlist->rdiff (cmds);
1403 _session->add_commands (cmds);
1404 _session->add_command (new StatefulDiffCommand (playlist));
1405 what_we_got->release ();
1407 break;
1411 bool
1412 RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
1414 if (!is_track()) {
1415 return false;
1418 boost::shared_ptr<Playlist> pl = playlist ();
1419 PlaylistSelection::iterator p;
1421 for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
1423 if (p == selection.playlists.end()) {
1424 return false;
1427 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos));
1429 if (track()->speed() != 1.0f) {
1430 pos = session_frame_to_track_frame (pos, track()->speed());
1431 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
1434 pl->clear_changes ();
1435 pl->paste (*p, pos, times);
1436 _session->add_command (new StatefulDiffCommand (pl));
1438 return true;
1442 struct PlaylistSorter {
1443 bool operator() (boost::shared_ptr<Playlist> a, boost::shared_ptr<Playlist> b) const {
1444 return a->sort_id() < b->sort_id();
1448 void
1449 RouteTimeAxisView::build_playlist_menu ()
1451 using namespace Menu_Helpers;
1453 if (!is_track()) {
1454 return;
1457 delete playlist_action_menu;
1458 playlist_action_menu = new Menu;
1459 playlist_action_menu->set_name ("ArdourContextMenu");
1461 MenuList& playlist_items = playlist_action_menu->items();
1462 playlist_action_menu->set_name ("ArdourContextMenu");
1463 playlist_items.clear();
1465 vector<boost::shared_ptr<Playlist> > playlists, playlists_tr;
1466 boost::shared_ptr<Track> tr = track();
1467 RadioMenuItem::Group playlist_group;
1469 _session->playlists->get (playlists);
1471 /* find the playlists for this diskstream */
1472 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
1473 if (((*i)->get_orig_diskstream_id() == tr->diskstream_id()) || (tr->playlist()->id() == (*i)->id())) {
1474 playlists_tr.push_back(*i);
1478 /* sort the playlists */
1479 PlaylistSorter cmp;
1480 sort (playlists_tr.begin(), playlists_tr.end(), cmp);
1482 /* add the playlists to the menu */
1483 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists_tr.begin(); i != playlists_tr.end(); ++i) {
1484 playlist_items.push_back (RadioMenuElem (playlist_group, (*i)->name()));
1485 RadioMenuItem *item = static_cast<RadioMenuItem*>(&playlist_items.back());
1486 item->signal_toggled().connect(sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::use_playlist), item, boost::weak_ptr<Playlist> (*i)));
1488 if (tr->playlist()->id() == (*i)->id()) {
1489 item->set_active();
1494 playlist_items.push_back (SeparatorElem());
1495 playlist_items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &RouteTimeAxisView::rename_current_playlist)));
1496 playlist_items.push_back (SeparatorElem());
1498 if (!route_group() || !route_group()->is_active() || !route_group()->enabled_property (ARDOUR::Properties::edit.property_id)) {
1499 playlist_items.push_back (MenuElem (_("New..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1500 playlist_items.push_back (MenuElem (_("New Copy..."), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1502 } else {
1503 // Use a label which tells the user what is happening
1504 playlist_items.push_back (MenuElem (_("New Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::new_playlists), this)));
1505 playlist_items.push_back (MenuElem (_("Copy Take"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::copy_playlists), this)));
1509 playlist_items.push_back (SeparatorElem());
1510 playlist_items.push_back (MenuElem (_("Clear Current"), sigc::bind(sigc::mem_fun(_editor, &PublicEditor::clear_playlists), this)));
1511 playlist_items.push_back (SeparatorElem());
1513 playlist_items.push_back (MenuElem(_("Select From All..."), sigc::mem_fun(*this, &RouteTimeAxisView::show_playlist_selector)));
1516 void
1517 RouteTimeAxisView::use_playlist (RadioMenuItem *item, boost::weak_ptr<Playlist> wpl)
1519 assert (is_track());
1521 // exit if we were triggered by deactivating the old playlist
1522 if (!item->get_active()) {
1523 return;
1526 boost::shared_ptr<Playlist> pl (wpl.lock());
1528 if (!pl) {
1529 return;
1532 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
1534 if (apl) {
1535 if (track()->playlist() == apl) {
1536 // exit when use_playlist is called by the creation of the playlist menu
1537 // or the playlist choice is unchanged
1538 return;
1540 track()->use_playlist (apl);
1542 RouteGroup* rg = route_group();
1544 if (rg && rg->is_active() && rg->enabled_property (ARDOUR::Properties::edit.property_id)) {
1545 std::string group_string = "." + rg->name() + ".";
1547 std::string take_name = apl->name();
1548 std::string::size_type idx = take_name.find(group_string);
1550 if (idx == std::string::npos)
1551 return;
1553 take_name = take_name.substr(idx + group_string.length()); // find the bit containing the take number / name
1555 boost::shared_ptr<RouteList> rl (rg->route_list());
1557 for (RouteList::const_iterator i = rl->begin(); i != rl->end(); ++i) {
1558 if ( (*i) == this->route()) {
1559 continue;
1562 std::string playlist_name = (*i)->name()+group_string+take_name;
1564 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track>(*i);
1565 if (!track) {
1566 continue;
1569 boost::shared_ptr<Playlist> ipl = session()->playlists->by_name(playlist_name);
1570 if (!ipl) {
1571 // No playlist for this track for this take yet, make it
1572 track->use_new_playlist();
1573 track->playlist()->set_name(playlist_name);
1574 } else {
1575 track->use_playlist(ipl);
1582 void
1583 RouteTimeAxisView::show_playlist_selector ()
1585 _editor.playlist_selector().show_for (this);
1588 void
1589 RouteTimeAxisView::map_frozen ()
1591 if (!is_track()) {
1592 return;
1595 ENSURE_GUI_THREAD (*this, &RouteTimeAxisView::map_frozen)
1597 switch (track()->freeze_state()) {
1598 case Track::Frozen:
1599 playlist_button.set_sensitive (false);
1600 rec_enable_button->set_sensitive (false);
1601 break;
1602 default:
1603 playlist_button.set_sensitive (true);
1604 rec_enable_button->set_sensitive (true);
1605 break;
1609 void
1610 RouteTimeAxisView::color_handler ()
1612 //case cTimeStretchOutline:
1613 if (timestretch_rect) {
1614 timestretch_rect->property_outline_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchOutline.get();
1616 //case cTimeStretchFill:
1617 if (timestretch_rect) {
1618 timestretch_rect->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_TimeStretchFill.get();
1621 reset_meter();
1624 /** Toggle an automation track for a fully-specified Parameter (type,channel,id)
1625 * Will add track if necessary.
1627 void
1628 RouteTimeAxisView::toggle_automation_track (const Evoral::Parameter& param)
1630 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1631 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1633 if (!track) {
1634 /* it doesn't exist yet, so we don't care about the button state: just add it */
1635 create_automation_child (param, true);
1636 } else {
1637 assert (menu);
1638 bool yn = menu->get_active();
1639 if (track->set_visibility (menu->get_active()) && yn) {
1641 /* we made it visible, now trigger a redisplay. if it was hidden, then automation_track_hidden()
1642 will have done that for us.
1645 if (!no_redraw) {
1646 _route->gui_changed (X_("track_height"), (void *) 0); /* EMIT_SIGNAL */
1652 void
1653 RouteTimeAxisView::automation_track_hidden (Evoral::Parameter param)
1655 boost::shared_ptr<AutomationTimeAxisView> track = automation_child (param);
1657 if (!track) {
1658 return;
1661 Gtk::CheckMenuItem* menu = automation_child_menu_item (param);
1663 if (menu && !_hidden) {
1664 ignore_toggle = true;
1665 menu->set_active (false);
1666 ignore_toggle = false;
1669 if (_route && !no_redraw) {
1670 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1675 void
1676 RouteTimeAxisView::show_all_automation (bool apply_to_selection)
1678 if (apply_to_selection) {
1679 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_all_automation, _1, false));
1680 } else {
1681 no_redraw = true;
1683 /* Show our automation */
1685 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1686 i->second->set_visibility (true);
1688 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1690 if (menu) {
1691 menu->set_active(true);
1696 /* Show processor automation */
1698 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1699 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1700 if ((*ii)->view == 0) {
1701 add_processor_automation_curve ((*i)->processor, (*ii)->what);
1704 (*ii)->menu_item->set_active (true);
1708 no_redraw = false;
1710 /* Redraw */
1712 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1716 void
1717 RouteTimeAxisView::show_existing_automation (bool apply_to_selection)
1719 if (apply_to_selection) {
1720 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::show_existing_automation, _1, false));
1721 } else {
1722 no_redraw = true;
1724 /* Show our automation */
1726 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1727 if (i->second->has_automation()) {
1728 i->second->set_visibility (true);
1730 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1731 if (menu) {
1732 menu->set_active(true);
1737 /* Show processor automation */
1739 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1740 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1741 if ((*ii)->view != 0 && (*i)->processor->control((*ii)->what)->list()->size() > 0) {
1742 (*ii)->menu_item->set_active (true);
1747 no_redraw = false;
1749 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1753 void
1754 RouteTimeAxisView::hide_all_automation (bool apply_to_selection)
1756 if (apply_to_selection) {
1757 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::hide_all_automation, _1, false));
1758 } else {
1759 no_redraw = true;
1761 /* Hide our automation */
1763 for (AutomationTracks::iterator i = _automation_tracks.begin(); i != _automation_tracks.end(); ++i) {
1764 i->second->set_visibility (false);
1766 Gtk::CheckMenuItem* menu = automation_child_menu_item (i->first);
1768 if (menu) {
1769 menu->set_active (false);
1773 /* Hide processor automation */
1775 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1776 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1777 (*ii)->menu_item->set_active (false);
1781 no_redraw = false;
1782 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1787 void
1788 RouteTimeAxisView::region_view_added (RegionView* rv)
1790 /* XXX need to find out if automation children have automationstreamviews. If yes, no ghosts */
1791 for (Children::iterator i = children.begin(); i != children.end(); ++i) {
1792 boost::shared_ptr<AutomationTimeAxisView> atv;
1794 if ((atv = boost::dynamic_pointer_cast<AutomationTimeAxisView> (*i)) != 0) {
1795 atv->add_ghost(rv);
1799 for (UnderlayMirrorList::iterator i = _underlay_mirrors.begin(); i != _underlay_mirrors.end(); ++i) {
1800 (*i)->add_ghost(rv);
1804 RouteTimeAxisView::ProcessorAutomationInfo::~ProcessorAutomationInfo ()
1806 for (vector<ProcessorAutomationNode*>::iterator i = lines.begin(); i != lines.end(); ++i) {
1807 delete *i;
1812 RouteTimeAxisView::ProcessorAutomationNode::~ProcessorAutomationNode ()
1814 parent.remove_processor_automation_node (this);
1817 void
1818 RouteTimeAxisView::remove_processor_automation_node (ProcessorAutomationNode* pan)
1820 if (pan->view) {
1821 remove_child (pan->view);
1825 RouteTimeAxisView::ProcessorAutomationNode*
1826 RouteTimeAxisView::find_processor_automation_node (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1828 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
1830 if ((*i)->processor == processor) {
1832 for (vector<ProcessorAutomationNode*>::iterator ii = (*i)->lines.begin(); ii != (*i)->lines.end(); ++ii) {
1833 if ((*ii)->what == what) {
1834 return *ii;
1840 return 0;
1843 void
1844 RouteTimeAxisView::add_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
1846 string name;
1847 ProcessorAutomationNode* pan;
1849 if ((pan = find_processor_automation_node (processor, what)) == 0) {
1850 /* session state may never have been saved with new plugin */
1851 error << _("programming error: ")
1852 << string_compose (X_("processor automation curve for %1:%2/%3/%4 not registered with track!"),
1853 processor->name(), what.type(), (int) what.channel(), what.id() )
1854 << endmsg;
1855 /*NOTREACHED*/
1856 return;
1859 if (pan->view) {
1860 return;
1863 boost::shared_ptr<AutomationControl> control
1864 = boost::dynamic_pointer_cast<AutomationControl>(processor->control(what, true));
1866 pan->view = boost::shared_ptr<AutomationTimeAxisView>(
1867 new AutomationTimeAxisView (_session, _route, processor, control, control->parameter (),
1868 _editor, *this, false, parent_canvas,
1869 processor->describe_parameter (what), processor->name()));
1871 pan->view->Hiding.connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_automation_track_hidden), pan, processor));
1873 add_automation_child (control->parameter(), pan->view, pan->view->marked_for_display ());
1875 if (_view) {
1876 _view->foreach_regionview (sigc::mem_fun(*pan->view.get(), &TimeAxisView::add_ghost));
1880 void
1881 RouteTimeAxisView::processor_automation_track_hidden (RouteTimeAxisView::ProcessorAutomationNode* pan, boost::shared_ptr<Processor> i)
1883 if (!_hidden) {
1884 pan->menu_item->set_active (false);
1887 if (!no_redraw) {
1888 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1892 void
1893 RouteTimeAxisView::add_existing_processor_automation_curves (boost::weak_ptr<Processor> p)
1895 boost::shared_ptr<Processor> processor (p.lock ());
1897 if (!processor) {
1898 return;
1901 set<Evoral::Parameter> existing;
1903 processor->what_has_data (existing);
1905 for (set<Evoral::Parameter>::iterator i = existing.begin(); i != existing.end(); ++i) {
1907 Evoral::Parameter param (*i);
1908 boost::shared_ptr<AutomationLine> al;
1910 if ((al = find_processor_automation_curve (processor, param)) != 0) {
1911 al->queue_reset ();
1912 } else {
1913 add_processor_automation_curve (processor, param);
1918 void
1919 RouteTimeAxisView::add_automation_child (Evoral::Parameter param, boost::shared_ptr<AutomationTimeAxisView> track, bool show)
1921 using namespace Menu_Helpers;
1923 XMLProperty* prop;
1924 XMLNode* node;
1926 add_child (track);
1928 track->Hiding.connect (sigc::bind (sigc::mem_fun (*this, &RouteTimeAxisView::automation_track_hidden), param));
1930 _automation_tracks[param] = track;
1932 if ((node = track->get_state_node()) != 0) {
1933 if ((prop = node->property ("shown")) != 0) {
1934 /* existing state overrides "show" argument */
1935 show = string_is_affirmative (prop->value());
1939 track->set_visibility (show);
1941 if (!no_redraw) {
1942 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
1945 if (!EventTypeMap::instance().is_midi_parameter(param)) {
1946 /* MIDI-related parameters are always in the menu, there's no
1947 reason to rebuild the menu just because we added a automation
1948 lane for one of them. But if we add a non-MIDI automation
1949 lane, then we need to invalidate the display menu.
1951 delete display_menu;
1952 display_menu = 0;
1956 void
1957 RouteTimeAxisView::add_processor_to_subplugin_menu (boost::weak_ptr<Processor> p)
1959 boost::shared_ptr<Processor> processor (p.lock ());
1961 if (!processor || !processor->display_to_user ()) {
1962 return;
1965 /* we use this override to veto the Amp processor from the plugin menu,
1966 as its automation lane can be accessed using the special "Fader" menu
1967 option
1970 if (boost::dynamic_pointer_cast<Amp> (processor) != 0) {
1971 return;
1974 using namespace Menu_Helpers;
1975 ProcessorAutomationInfo *rai;
1976 list<ProcessorAutomationInfo*>::iterator x;
1978 const std::set<Evoral::Parameter>& automatable = processor->what_can_be_automated ();
1980 if (automatable.empty()) {
1981 return;
1984 for (x = processor_automation.begin(); x != processor_automation.end(); ++x) {
1985 if ((*x)->processor == processor) {
1986 break;
1990 if (x == processor_automation.end()) {
1992 rai = new ProcessorAutomationInfo (processor);
1993 processor_automation.push_back (rai);
1995 } else {
1997 rai = *x;
2001 /* any older menu was deleted at the top of processors_changed()
2002 when we cleared the subplugin menu.
2005 rai->menu = manage (new Menu);
2006 MenuList& items = rai->menu->items();
2007 rai->menu->set_name ("ArdourContextMenu");
2009 items.clear ();
2011 std::set<Evoral::Parameter> has_visible_automation;
2012 AutomationTimeAxisView::what_has_visible_automation (processor, has_visible_automation);
2014 for (std::set<Evoral::Parameter>::const_iterator i = automatable.begin(); i != automatable.end(); ++i) {
2016 ProcessorAutomationNode* pan;
2017 CheckMenuItem* mitem;
2019 string name = processor->describe_parameter (*i);
2021 items.push_back (CheckMenuElem (name));
2022 mitem = dynamic_cast<CheckMenuItem*> (&items.back());
2024 _subplugin_menu_map[*i] = mitem;
2026 if (has_visible_automation.find((*i)) != has_visible_automation.end()) {
2027 mitem->set_active(true);
2030 if ((pan = find_processor_automation_node (processor, *i)) == 0) {
2032 /* new item */
2034 pan = new ProcessorAutomationNode (*i, mitem, *this);
2036 rai->lines.push_back (pan);
2038 } else {
2040 pan->menu_item = mitem;
2044 mitem->signal_toggled().connect (sigc::bind (sigc::mem_fun(*this, &RouteTimeAxisView::processor_menu_item_toggled), rai, pan));
2047 /* add the menu for this processor, because the subplugin
2048 menu is always cleared at the top of processors_changed().
2049 this is the result of some poor design in gtkmm and/or
2050 GTK+.
2053 subplugin_menu.items().push_back (MenuElem (processor->name(), *rai->menu));
2054 rai->valid = true;
2057 void
2058 RouteTimeAxisView::processor_menu_item_toggled (RouteTimeAxisView::ProcessorAutomationInfo* rai,
2059 RouteTimeAxisView::ProcessorAutomationNode* pan)
2061 bool showit = pan->menu_item->get_active();
2062 bool redraw = false;
2064 if (pan->view == 0 && showit) {
2065 add_processor_automation_curve (rai->processor, pan->what);
2066 redraw = true;
2069 if (pan->view && showit != pan->view->marked_for_display()) {
2070 pan->view->set_visibility (showit);
2071 redraw = true;
2074 if (redraw && !no_redraw) {
2075 _route->gui_changed ("track_height", (void *) 0); /* EMIT_SIGNAL */
2079 void
2080 RouteTimeAxisView::processors_changed (RouteProcessorChange c)
2082 if (c.type == RouteProcessorChange::MeterPointChange) {
2083 /* nothing to do if only the meter point has changed */
2084 return;
2087 using namespace Menu_Helpers;
2089 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ++i) {
2090 (*i)->valid = false;
2093 _subplugin_menu_map.clear ();
2094 subplugin_menu.items().clear ();
2096 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_processor_to_subplugin_menu));
2097 _route->foreach_processor (sigc::mem_fun (*this, &RouteTimeAxisView::add_existing_processor_automation_curves));
2099 bool deleted_processor_automation = false;
2101 for (list<ProcessorAutomationInfo*>::iterator i = processor_automation.begin(); i != processor_automation.end(); ) {
2103 list<ProcessorAutomationInfo*>::iterator tmp;
2105 tmp = i;
2106 ++tmp;
2108 if (!(*i)->valid) {
2110 delete *i;
2111 processor_automation.erase (i);
2112 deleted_processor_automation = true;
2116 i = tmp;
2119 if (deleted_processor_automation && !no_redraw) {
2120 _route->gui_changed ("track_height", this);
2124 boost::shared_ptr<AutomationLine>
2125 RouteTimeAxisView::find_processor_automation_curve (boost::shared_ptr<Processor> processor, Evoral::Parameter what)
2127 ProcessorAutomationNode* pan;
2129 if ((pan = find_processor_automation_node (processor, what)) != 0) {
2130 if (pan->view) {
2131 pan->view->line();
2135 return boost::shared_ptr<AutomationLine>();
2138 void
2139 RouteTimeAxisView::reset_processor_automation_curves ()
2141 for (ProcessorAutomationCurves::iterator i = processor_automation_curves.begin(); i != processor_automation_curves.end(); ++i) {
2142 (*i)->reset();
2146 void
2147 RouteTimeAxisView::update_rec_display ()
2149 RouteUI::update_rec_display ();
2150 name_entry.set_sensitive (!_route->record_enabled());
2153 void
2154 RouteTimeAxisView::set_layer_display (LayerDisplay d, bool apply_to_selection)
2156 if (apply_to_selection) {
2157 _editor.get_selection().tracks.foreach_route_time_axis (boost::bind (&RouteTimeAxisView::set_layer_display, _1, d, false));
2158 } else {
2160 if (_view) {
2161 _view->set_layer_display (d);
2164 ensure_xml_node ();
2165 xml_node->add_property (N_("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 if (update_xml) {
2315 if (!underlay_xml_node) {
2316 ensure_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());
2327 void
2328 RouteTimeAxisView::remove_underlay (StreamView* v)
2330 if (!v) {
2331 return;
2334 UnderlayList::iterator it = find(_underlay_streams.begin(), _underlay_streams.end(), v);
2335 RouteTimeAxisView& other = v->trackview();
2337 if (it != _underlay_streams.end()) {
2338 UnderlayMirrorList::iterator gm = find(other._underlay_mirrors.begin(), other._underlay_mirrors.end(), this);
2340 if (gm == other._underlay_mirrors.end()) {
2341 fatal << _("programming error: underlay reference pointer pairs are inconsistent!") << endmsg;
2342 /*NOTREACHED*/
2345 v->foreach_regionview(sigc::mem_fun(*this, &RouteTimeAxisView::remove_ghost));
2347 _underlay_streams.erase(it);
2348 other._underlay_mirrors.erase(gm);
2350 if (underlay_xml_node) {
2351 underlay_xml_node->remove_nodes_and_delete("id", v->trackview().route()->id().to_s());
2356 void
2357 RouteTimeAxisView::set_button_names ()
2359 rec_enable_button_label.set_text (_("r"));
2361 if (_route && _route->solo_safe()) {
2362 solo_button_label.set_text (X_("!"));
2363 } else {
2364 if (Config->get_solo_control_is_listen_control()) {
2365 switch (Config->get_listen_position()) {
2366 case AfterFaderListen:
2367 solo_button_label.set_text (_("A"));
2368 break;
2369 case PreFaderListen:
2370 solo_button_label.set_text (_("P"));
2371 break;
2373 } else {
2374 solo_button_label.set_text (_("s"));
2377 mute_button_label.set_text (_("m"));
2380 Gtk::CheckMenuItem*
2381 RouteTimeAxisView::automation_child_menu_item (Evoral::Parameter param)
2383 ParameterMenuMap::iterator i = _main_automation_menu_map.find (param);
2384 if (i != _main_automation_menu_map.end()) {
2385 return i->second;
2388 i = _subplugin_menu_map.find (param);
2389 if (i != _subplugin_menu_map.end()) {
2390 return i->second;
2393 return 0;
2396 void
2397 RouteTimeAxisView::create_gain_automation_child (const Evoral::Parameter& param, bool show)
2399 boost::shared_ptr<AutomationControl> c = _route->gain_control();
2400 if (!c) {
2401 error << "Route has no gain automation, unable to add automation track view." << endmsg;
2402 return;
2405 gain_track.reset (new AutomationTimeAxisView (_session,
2406 _route, _route->amp(), c, param,
2407 _editor,
2408 *this,
2409 false,
2410 parent_canvas,
2411 _route->amp()->describe_parameter(param)));
2413 if (_view) {
2414 _view->foreach_regionview (sigc::mem_fun (*gain_track.get(), &TimeAxisView::add_ghost));
2417 add_automation_child (Evoral::Parameter(GainAutomation), gain_track, show);
2420 static
2421 void add_region_to_list (RegionView* rv, Playlist::RegionList* l)
2423 l->push_back (rv->region());
2426 RegionView*
2427 RouteTimeAxisView::combine_regions ()
2429 /* as of may 2011, we do not offer uncombine for MIDI tracks
2432 if (!is_audio_track()) {
2433 return 0;
2436 if (!_view) {
2437 return 0;
2440 Playlist::RegionList selected_regions;
2441 boost::shared_ptr<Playlist> playlist = track()->playlist();
2443 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2445 if (selected_regions.size() < 2) {
2446 return 0;
2449 playlist->clear_changes ();
2450 boost::shared_ptr<Region> compound_region = playlist->combine (selected_regions);
2452 _session->add_command (new StatefulDiffCommand (playlist));
2453 /* make the new region be selected */
2455 return _view->find_view (compound_region);
2458 void
2459 RouteTimeAxisView::uncombine_regions ()
2461 /* as of may 2011, we do not offer uncombine for MIDI tracks
2463 if (!is_audio_track()) {
2464 return;
2467 if (!_view) {
2468 return;
2471 Playlist::RegionList selected_regions;
2472 boost::shared_ptr<Playlist> playlist = track()->playlist();
2474 /* have to grab selected regions first because the uncombine is going
2475 * to change that in the middle of the list traverse
2478 _view->foreach_selected_regionview (sigc::bind (sigc::ptr_fun (add_region_to_list), &selected_regions));
2480 playlist->clear_changes ();
2482 for (Playlist::RegionList::iterator i = selected_regions.begin(); i != selected_regions.end(); ++i) {
2483 playlist->uncombine (*i);
2486 _session->add_command (new StatefulDiffCommand (playlist));