1 /*
2 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
3 * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
4 * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
6 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
7 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
8 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 */
24
25 #include <algorithm>
26 #include <cstdlib>
27
28 #include "pbd/unwind.h"
29
30 #include "ardour/control_protocol_manager.h"
31 #include "ardour/midi_region.h"
32 #include "ardour/playlist.h"
33 #include "ardour/profile.h"
34 #include "ardour/route_group.h"
35 #include "ardour/selection.h"
36 #include "ardour/session.h"
37 #include "ardour/vca.h"
38
39 #include "editor.h"
40 #include "editor_drag.h"
41 #include "editor_routes.h"
42 #include "editor_sources.h"
43 #include "actions.h"
44 #include "audio_time_axis.h"
45 #include "audio_region_view.h"
46 #include "audio_streamview.h"
47 #include "automation_line.h"
48 #include "control_point.h"
49 #include "editor_regions.h"
50 #include "editor_cursors.h"
51 #include "keyboard.h"
52 #include "midi_region_view.h"
53 #include "sfdb_ui.h"
54
55 #include "pbd/i18n.h"
56
57 using namespace std;
58 using namespace ARDOUR;
59 using namespace PBD;
60 using namespace Gtk;
61 using namespace Glib;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
64
65 struct TrackViewByPositionSorter
66 {
operator ()TrackViewByPositionSorter67 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
68 return a->y_position() < b->y_position();
69 }
70 };
71
72 bool
extend_selection_to_track(TimeAxisView & view)73 Editor::extend_selection_to_track (TimeAxisView& view)
74 {
75 if (selection->selected (&view)) {
76 /* already selected, do nothing */
77 return false;
78 }
79
80 if (selection->tracks.empty()) {
81
82 if (!selection->selected (&view)) {
83 selection->set (&view);
84 return true;
85 } else {
86 return false;
87 }
88 }
89
90 /* something is already selected, so figure out which range of things to add */
91
92 TrackViewList to_be_added;
93 TrackViewList sorted = track_views;
94 TrackViewByPositionSorter cmp;
95 bool passed_clicked = false;
96 bool forwards = true;
97
98 sorted.sort (cmp);
99
100 /* figure out if we should go forward or backwards */
101
102 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
103
104 if ((*i) == &view) {
105 passed_clicked = true;
106 }
107
108 if (selection->selected (*i)) {
109 if (passed_clicked) {
110 forwards = true;
111 } else {
112 forwards = false;
113 }
114 break;
115 }
116 }
117
118 passed_clicked = false;
119
120 if (forwards) {
121
122 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
123
124 if ((*i) == &view) {
125 passed_clicked = true;
126 continue;
127 }
128
129 if (passed_clicked) {
130 if ((*i)->hidden()) {
131 continue;
132 }
133 if (selection->selected (*i)) {
134 break;
135 } else if (!(*i)->hidden()) {
136 to_be_added.push_back (*i);
137 }
138 }
139 }
140
141 } else {
142
143 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
144
145 if ((*r) == &view) {
146 passed_clicked = true;
147 continue;
148 }
149
150 if (passed_clicked) {
151
152 if ((*r)->hidden()) {
153 continue;
154 }
155
156 if (selection->selected (*r)) {
157 break;
158 } else if (!(*r)->hidden()) {
159 to_be_added.push_back (*r);
160 }
161 }
162 }
163 }
164
165 if (!selection->selected (&view)) {
166 to_be_added.push_back (&view);
167 }
168
169 if (!to_be_added.empty()) {
170 selection->add (to_be_added);
171 return true;
172 }
173
174 return false;
175 }
176
177 void
select_all_tracks()178 Editor::select_all_tracks ()
179 {
180 TrackViewList tracks;
181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
182 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i);
183 if ( rtv && rtv->route()->is_track() ) {
184 tracks.push_back (*i);
185 }
186 }
187 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
188 selection->set (tracks);
189 }
190
191 void
select_all_visible_lanes()192 Editor::select_all_visible_lanes ()
193 {
194 TrackViewList visible_views;
195 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
196 if ((*i)->marked_for_display()) {
197 visible_views.push_back (*i);
198 }
199 }
200 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
201 selection->set (visible_views);
202 }
203
204 /** Select clicked_axisview, unless there are no currently selected
205 * tracks, in which case nothing will happen unless `force' is true.
206 */
207 void
set_selected_track_as_side_effect(Selection::Operation op)208 Editor::set_selected_track_as_side_effect (Selection::Operation op)
209 {
210 if (!clicked_axisview) {
211 return;
212 }
213
214 PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
215
216 RouteGroup* group = NULL;
217 if (clicked_routeview) {
218 group = clicked_routeview->route()->route_group();
219 }
220
221 switch (op) {
222 case Selection::Toggle:
223 if (selection->selected (clicked_axisview)) {
224 if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
225 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
226 if ((*i)->route_group() == group) {
227 selection->remove(*i);
228 }
229 }
230 } else {
231 selection->remove (clicked_axisview);
232 }
233 } else {
234 if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
235 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 if ((*i)->route_group() == group) {
237 selection->add(*i);
238 }
239 }
240 } else {
241 selection->add (clicked_axisview);
242 }
243 }
244 break;
245
246 case Selection::Add:
247 if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
248 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
249 if ((*i)->route_group() == group) {
250 selection->add(*i);
251 }
252 }
253 } else {
254 selection->add (clicked_axisview);
255 }
256 break;
257
258 case Selection::Set:
259 selection->clear();
260 if (group && group->is_active() && group->enabled_property(ARDOUR::Properties::group_select.property_id)) {
261 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
262 if ((*i)->route_group() == group) {
263 selection->add(*i);
264 }
265 }
266 } else {
267 selection->set (clicked_axisview);
268 }
269 break;
270
271 case Selection::Extend:
272 selection->clear();
273 break;
274 }
275 }
276
277 void
set_selected_track(TimeAxisView & view,Selection::Operation op,bool no_remove)278 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
279 {
280 begin_reversible_selection_op (X_("Set Selected Track"));
281
282 switch (op) {
283 case Selection::Toggle:
284 if (selection->selected (&view)) {
285 if (!no_remove) {
286 selection->remove (&view);
287 }
288 } else {
289 selection->add (&view);
290 }
291 break;
292
293 case Selection::Add:
294 selection->add (&view);
295 break;
296
297 case Selection::Set:
298 selection->set (&view);
299 break;
300
301 case Selection::Extend:
302 extend_selection_to_track (view);
303 break;
304 }
305
306 commit_reversible_selection_op ();
307 }
308
309 void
set_selected_track_from_click(bool press,Selection::Operation op,bool no_remove)310 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
311 {
312 if (!clicked_routeview) {
313 return;
314 }
315
316 if (!press) {
317 return;
318 }
319
320 set_selected_track (*clicked_routeview, op, no_remove);
321 }
322
323 bool
set_selected_control_point_from_click(bool press,Selection::Operation op)324 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
325 {
326 if (!clicked_control_point) {
327 return false;
328 }
329
330 bool ret = false;
331
332 switch (op) {
333 case Selection::Set:
334 if (!selection->selected (clicked_control_point)) {
335 selection->set (clicked_control_point);
336 ret = true;
337 } else {
338 /* clicked on an already selected point */
339 if (press) {
340 break;
341 } else {
342 if (selection->points.size() > 1) {
343 selection->set (clicked_control_point);
344 ret = true;
345 }
346 }
347 }
348 break;
349
350 case Selection::Add:
351 if (press) {
352 selection->add (clicked_control_point);
353 ret = true;
354 }
355 break;
356 case Selection::Toggle:
357
358 /* This is a bit of a hack; if we Primary-Click-Drag a control
359 point (for push drag) we want the point we clicked on to be
360 selected, otherwise we end up confusingly dragging an
361 unselected point. So here we ensure that the point is selected
362 after the press, and if we subsequently get a release (meaning no
363 drag occurred) we set things up so that the toggle has happened.
364 */
365 if (press && !selection->selected (clicked_control_point)) {
366 /* This is the button press, and the control point is not selected; make it so,
367 in case this press leads to a drag. Also note that having done this, we don't
368 need to toggle again on release.
369 */
370 selection->toggle (clicked_control_point);
371 _control_point_toggled_on_press = true;
372 ret = true;
373 } else if (!press && !_control_point_toggled_on_press) {
374 /* This is the release, and the point wasn't toggled on the press, so do it now */
375 selection->toggle (clicked_control_point);
376 ret = true;
377 } else {
378 /* Reset our flag */
379 _control_point_toggled_on_press = false;
380 }
381 break;
382 case Selection::Extend:
383 /* XXX */
384 break;
385 }
386
387 return ret;
388 }
389
390 void
get_onscreen_tracks(TrackViewList & tvl)391 Editor::get_onscreen_tracks (TrackViewList& tvl)
392 {
393 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
394 if ((*i)->y_position() < _visible_canvas_height) {
395 tvl.push_back (*i);
396 }
397 }
398 }
399
400 /** Call a slot for a given `basis' track and also for any track that is in the same
401 * active route group with a particular set of properties.
402 *
403 * @param sl Slot to call.
404 * @param basis Basis track.
405 * @param prop Properties that active edit groups must share to be included in the map.
406 */
407
408 void
mapover_grouped_routes(sigc::slot<void,RouteUI &> sl,RouteUI * basis,PBD::PropertyID prop) const409 Editor::mapover_grouped_routes (sigc::slot<void, RouteUI&> sl, RouteUI* basis, PBD::PropertyID prop) const
410 {
411 set<RouteUI*> routes;
412
413 routes.insert(basis);
414
415 RouteGroup* group = basis->route()->route_group();
416
417 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
418
419 /* the basis is a member of an active route group, with the appropriate
420 * properties; find other members */
421
422 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
423 RouteUI* v = dynamic_cast<RouteUI*> (*i);
424 if ( v && (v->route() != basis->route()) && v->route()->route_group() == group) {
425 routes.insert (v);
426 }
427 }
428 }
429
430 /* call the slots */
431 for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
432 sl (**i);
433 }
434 }
435
436 void
mapover_armed_routes(sigc::slot<void,RouteUI &> sl) const437 Editor::mapover_armed_routes (sigc::slot<void, RouteUI&> sl) const
438 {
439 set<RouteUI*> routes;
440 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
441 RouteUI* v = dynamic_cast<RouteUI*> (*i);
442 if (v && v->route()->is_track()) {
443 if ( v->track()->rec_enable_control()->get_value()) {
444 routes.insert (v);
445 }
446 }
447 }
448 for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
449 sl (**i);
450 }
451 }
452
453 void
mapover_selected_routes(sigc::slot<void,RouteUI &> sl) const454 Editor::mapover_selected_routes (sigc::slot<void, RouteUI&> sl) const
455 {
456 set<RouteUI*> routes;
457 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
458 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
459 if (r) {
460 routes.insert (r);
461 }
462 }
463 for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
464 sl (**i);
465 }
466 }
467
468 void
mapover_all_routes(sigc::slot<void,RouteUI &> sl) const469 Editor::mapover_all_routes (sigc::slot<void, RouteUI&> sl) const
470 {
471 set<RouteUI*> routes;
472 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
473 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
474 if (r) {
475 routes.insert (r);
476 }
477 }
478 for (set<RouteUI*>::iterator i = routes.begin(); i != routes.end(); ++i) {
479 sl (**i);
480 }
481 }
482
483 /** Call a slot for a given `basis' track and also for any track that is in the same
484 * active route group with a particular set of properties.
485 *
486 * @param sl Slot to call.
487 * @param basis Basis track.
488 * @param prop Properties that active edit groups must share to be included in the map.
489 */
490
491 void
mapover_tracks_with_unique_playlists(sigc::slot<void,RouteTimeAxisView &,uint32_t> sl,TimeAxisView * basis,PBD::PropertyID prop) const492 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
493 {
494 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
495 set<boost::shared_ptr<Playlist> > playlists;
496
497 if (route_basis == 0) {
498 return;
499 }
500
501 set<RouteTimeAxisView*> tracks;
502 tracks.insert (route_basis);
503
504 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
505
506 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
507
508 /* the basis is a member of an active route group, with the appropriate
509 properties; find other members */
510
511 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
512 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
513
514 if (v && v->route()->route_group() == group) {
515
516 boost::shared_ptr<Track> t = v->track();
517 if (t) {
518 if (playlists.insert (t->playlist()).second) {
519 /* haven't seen this playlist yet */
520 tracks.insert (v);
521 }
522 } else {
523 /* not actually a "Track", but a timeaxis view that
524 we should mapover anyway.
525 */
526 tracks.insert (v);
527 }
528 }
529 }
530 }
531
532 /* call the slots */
533 uint32_t const sz = tracks.size ();
534
535 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
536 sl (**i, sz);
537 }
538 }
539
540 void
mapped_get_equivalent_regions(RouteTimeAxisView & tv,uint32_t,RegionView * basis,vector<RegionView * > * all_equivs) const541 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
542 {
543 boost::shared_ptr<Playlist> pl;
544 vector<boost::shared_ptr<Region> > results;
545 RegionView* marv;
546 boost::shared_ptr<Track> tr;
547
548 if ((tr = tv.track()) == 0) {
549 /* bus */
550 return;
551 }
552
553 if (&tv == &basis->get_time_axis_view()) {
554 /* looking in same track as the original */
555 return;
556 }
557
558 if ((pl = tr->playlist()) != 0) {
559 pl->get_equivalent_regions (basis->region(), results);
560 }
561
562 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
563 if ((marv = tv.view()->find_view (*ir)) != 0) {
564 all_equivs->push_back (marv);
565 }
566 }
567 }
568
569 void
get_equivalent_regions(RegionView * basis,vector<RegionView * > & equivalent_regions,PBD::PropertyID property) const570 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
571 {
572 mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
573
574 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
575
576 equivalent_regions.push_back (basis);
577 }
578
579 RegionSelection
get_equivalent_regions(RegionSelection & basis,PBD::PropertyID prop) const580 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
581 {
582 RegionSelection equivalent;
583
584 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
585
586 vector<RegionView*> eq;
587
588 mapover_tracks_with_unique_playlists (
589 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
590 &(*i)->get_time_axis_view(), prop);
591
592 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
593 equivalent.add (*j);
594 }
595
596 equivalent.add (*i);
597 }
598
599 return equivalent;
600 }
601
602 bool
set_selected_regionview_from_click(bool press,Selection::Operation op)603 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
604 {
605 vector<RegionView*> all_equivalent_regions;
606 bool commit = false;
607
608 if (!clicked_regionview || !clicked_routeview) {
609 return false;
610 }
611
612 if (press) {
613 button_release_can_deselect = false;
614 }
615
616 if (op == Selection::Toggle || op == Selection::Set) {
617
618 switch (op) {
619 case Selection::Toggle:
620 if (selection->selected (clicked_regionview)) {
621 if (press) {
622
623 /* whatever was clicked was selected already; do nothing here but allow
624 the button release to deselect it
625 */
626
627 button_release_can_deselect = true;
628
629 } else {
630 if (button_release_can_deselect) {
631
632 /* just remove this one region, but only on a permitted button release */
633
634 selection->remove (clicked_regionview);
635 commit = true;
636
637 /* no more deselect action on button release till a new press
638 finds an already selected object.
639 */
640
641 button_release_can_deselect = false;
642 }
643 }
644
645 } else {
646
647 if (press) {
648
649 if (selection->selected (clicked_routeview)) {
650 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
651 } else {
652 all_equivalent_regions.push_back (clicked_regionview);
653 }
654
655 /* add all the equivalent regions, but only on button press */
656
657 if (!all_equivalent_regions.empty()) {
658 commit = true;
659 }
660
661 selection->add (all_equivalent_regions);
662 }
663 }
664 break;
665
666 case Selection::Set:
667 if (!selection->selected (clicked_regionview)) {
668 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
669 selection->set (all_equivalent_regions);
670 commit = true;
671 } else {
672 /* clicked on an already selected region */
673 if (press)
674 goto out;
675 else {
676 if (selection->regions.size() > 1) {
677 /* collapse region selection down to just this one region (and its equivalents) */
678 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
679 selection->set(all_equivalent_regions);
680 commit = true;
681 }
682 }
683 }
684 break;
685
686 default:
687 /* silly compiler */
688 break;
689 }
690
691 } else if (op == Selection::Extend) {
692
693 list<Selectable*> results;
694 samplepos_t last_sample;
695 samplepos_t first_sample;
696 bool same_track = false;
697
698 /* 1. find the last selected regionview in the track that was clicked in */
699
700 last_sample = 0;
701 first_sample = max_samplepos;
702
703 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
704 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
705
706 if ((*x)->region()->last_sample() > last_sample) {
707 last_sample = (*x)->region()->last_sample();
708 }
709
710 if ((*x)->region()->first_sample() < first_sample) {
711 first_sample = (*x)->region()->first_sample();
712 }
713
714 same_track = true;
715 }
716 }
717
718 if (same_track) {
719
720 /* 2. figure out the boundaries for our search for new objects */
721
722 switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
723 case Evoral::OverlapNone:
724 if (last_sample < clicked_regionview->region()->first_sample()) {
725 first_sample = last_sample;
726 last_sample = clicked_regionview->region()->last_sample();
727 } else {
728 last_sample = first_sample;
729 first_sample = clicked_regionview->region()->first_sample();
730 }
731 break;
732
733 case Evoral::OverlapExternal:
734 if (last_sample < clicked_regionview->region()->first_sample()) {
735 first_sample = last_sample;
736 last_sample = clicked_regionview->region()->last_sample();
737 } else {
738 last_sample = first_sample;
739 first_sample = clicked_regionview->region()->first_sample();
740 }
741 break;
742
743 case Evoral::OverlapInternal:
744 if (last_sample < clicked_regionview->region()->first_sample()) {
745 first_sample = last_sample;
746 last_sample = clicked_regionview->region()->last_sample();
747 } else {
748 last_sample = first_sample;
749 first_sample = clicked_regionview->region()->first_sample();
750 }
751 break;
752
753 case Evoral::OverlapStart:
754 case Evoral::OverlapEnd:
755 /* nothing to do except add clicked region to selection, since it
756 overlaps with the existing selection in this track.
757 */
758 break;
759 }
760
761 } else {
762
763 /* click in a track that has no regions selected, so extend vertically
764 to pick out all regions that are defined by the existing selection
765 plus this one.
766 */
767
768
769 first_sample = clicked_regionview->region()->position();
770 last_sample = clicked_regionview->region()->last_sample();
771
772 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
773 if ((*i)->region()->position() < first_sample) {
774 first_sample = (*i)->region()->position();
775 }
776 if ((*i)->region()->last_sample() + 1 > last_sample) {
777 last_sample = (*i)->region()->last_sample();
778 }
779 }
780 }
781
782 /* 2. find all the tracks we should select in */
783
784 set<RouteTimeAxisView*> relevant_tracks;
785
786 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
787 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
788 if (r) {
789 relevant_tracks.insert (r);
790 }
791 }
792
793 set<RouteTimeAxisView*> already_in_selection;
794
795 if (relevant_tracks.empty()) {
796
797 /* no tracks selected .. thus .. if the
798 regionview we're in isn't selected
799 (i.e. we're about to extend to it), then
800 find all tracks between the this one and
801 any selected ones.
802 */
803
804 if (!selection->selected (clicked_regionview)) {
805
806 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
807
808 if (rtv) {
809
810 /* add this track to the ones we will search */
811
812 relevant_tracks.insert (rtv);
813
814 /* find the track closest to this one that
815 already a selected region.
816 */
817
818 RouteTimeAxisView* closest = 0;
819 int distance = INT_MAX;
820 int key = rtv->route()->presentation_info().order ();
821
822 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
823
824 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
825
826 if (artv && artv != rtv) {
827
828 pair<set<RouteTimeAxisView*>::iterator,bool> result;
829
830 result = already_in_selection.insert (artv);
831
832 if (result.second) {
833 /* newly added to already_in_selection */
834
835 int d = artv->route()->presentation_info().order ();
836
837 d -= key;
838
839 if (abs (d) < distance) {
840 distance = abs (d);
841 closest = artv;
842 }
843 }
844 }
845 }
846
847 if (closest) {
848
849 /* now add all tracks between that one and this one */
850
851 int okey = closest->route()->presentation_info().order ();
852
853 if (okey > key) {
854 swap (okey, key);
855 }
856
857 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
858 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
859 if (artv && artv != rtv) {
860
861 int k = artv->route()->presentation_info().order ();
862
863 if (k >= okey && k <= key) {
864
865 /* in range but don't add it if
866 it already has tracks selected.
867 this avoids odd selection
868 behaviour that feels wrong.
869 */
870
871 if (find (already_in_selection.begin(),
872 already_in_selection.end(),
873 artv) == already_in_selection.end()) {
874
875 relevant_tracks.insert (artv);
876 }
877 }
878 }
879 }
880 }
881 }
882 }
883 }
884
885 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
886 one that was clicked.
887 */
888
889 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
890 (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
891 }
892
893 /* 4. convert to a vector of regions */
894
895 vector<RegionView*> regions;
896
897 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
898 RegionView* arv;
899
900 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
901 regions.push_back (arv);
902 }
903 }
904
905 if (!regions.empty()) {
906 selection->add (regions);
907 commit = true;
908 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
909 /* ensure that at least the clicked regionview is selected. */
910 selection->set (clicked_regionview);
911 commit = true;
912 }
913
914 }
915
916 out:
917 return commit;
918 }
919
920 void
set_selected_midi_region_view(MidiRegionView & mrv)921 Editor::set_selected_midi_region_view (MidiRegionView& mrv)
922 {
923 /* clear note selection in all currently selected MidiRegionViews */
924
925 if (get_selection().regions.contains (&mrv) && get_selection().regions.size() == 1) {
926 /* Nothing to do */
927 return;
928 }
929
930 midi_action (&MidiRegionView::clear_note_selection);
931 get_selection().set (&mrv);
932 }
933
934 void
set_selection(std::list<Selectable * > s,Selection::Operation op)935 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
936 {
937 if (s.empty()) {
938 return;
939 }
940 begin_reversible_selection_op (X_("set selection"));
941 switch (op) {
942 case Selection::Toggle:
943 selection->toggle (s);
944 break;
945 case Selection::Set:
946 selection->set (s);
947 break;
948 case Selection::Extend:
949 selection->add (s);
950 break;
951 case Selection::Add:
952 selection->add (s);
953 break;
954 }
955
956 commit_reversible_selection_op () ;
957 }
958
959 void
set_selected_regionview_from_region_list(boost::shared_ptr<Region> region,Selection::Operation op)960 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
961 {
962 vector<RegionView*> regionviews;
963
964 get_regionview_corresponding_to (region, regionviews);
965
966 if (regionviews.empty()) {
967 return;
968 }
969
970 begin_reversible_selection_op (X_("set selected regions"));
971
972 switch (op) {
973 case Selection::Toggle:
974 /* XXX this is not correct */
975 selection->toggle (regionviews);
976 break;
977 case Selection::Set:
978 selection->set (regionviews);
979 break;
980 case Selection::Extend:
981 selection->add (regionviews);
982 break;
983 case Selection::Add:
984 selection->add (regionviews);
985 break;
986 }
987
988 commit_reversible_selection_op () ;
989 }
990
991 bool
set_selected_regionview_from_map_event(GdkEventAny *,StreamView * sv,boost::weak_ptr<Region> weak_r)992 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
993 {
994 RegionView* rv;
995 boost::shared_ptr<Region> r (weak_r.lock());
996
997 if (!r) {
998 return true;
999 }
1000
1001 if ((rv = sv->find_view (r)) == 0) {
1002 return true;
1003 }
1004
1005 /* don't reset the selection if its something other than
1006 a single other region.
1007 */
1008
1009 if (selection->regions.size() > 1) {
1010 return true;
1011 }
1012
1013 begin_reversible_selection_op (X_("set selected regions"));
1014
1015 selection->set (rv);
1016
1017 commit_reversible_selection_op () ;
1018
1019 return true;
1020 }
1021
1022 void
presentation_info_changed(PropertyChange const & what_changed)1023 Editor::presentation_info_changed (PropertyChange const & what_changed)
1024 {
1025 uint32_t n_tracks = 0;
1026 uint32_t n_busses = 0;
1027 uint32_t n_vcas = 0;
1028 uint32_t n_routes = 0;
1029 uint32_t n_stripables = 0;
1030
1031 /* We cannot ensure ordering of the handlers for
1032 * PresentationInfo::Changed, so we have to do everything in order
1033 * here, as a single handler.
1034 */
1035
1036 if (what_changed.contains (Properties::selected)) {
1037 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1038 (*i)->set_selected (false);
1039 (*i)->hide_selection ();
1040 }
1041 }
1042
1043 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1044 * currently selected stripable/controllable duples are found and added
1045 */
1046
1047 selection->core_selection_changed (what_changed);
1048
1049 /* STEP 2: update TimeAxisView's knowledge of their selected state
1050 */
1051
1052 if (what_changed.contains (Properties::selected)) {
1053
1054 StripableNotificationListPtr stripables (new StripableNotificationList);
1055
1056 switch (selection->tracks.size()) {
1057 case 0:
1058 break;
1059 default:
1060 set_selected_mixer_strip (*(selection->tracks.back()));
1061 if (!_track_selection_change_without_scroll && !_editor_track_selection_change_without_scroll) {
1062 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1063 }
1064 break;
1065 }
1066
1067 CoreSelection::StripableAutomationControls sc;
1068 _session->selection().get_stripables (sc);
1069
1070 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1071
1072 AxisView* av = axis_view_by_stripable ((*i).stripable);
1073
1074 if (!av) {
1075 continue;
1076 }
1077
1078 n_stripables++;
1079
1080 if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1081 n_tracks++;
1082 n_routes++;
1083 } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1084 n_busses++;
1085 n_routes++;
1086 } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1087 n_vcas++;
1088 }
1089
1090 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1091
1092 if (!tav) {
1093 assert (0);
1094 continue; /* impossible */
1095 }
1096
1097 if (!(*i).controllable) {
1098
1099 /* "parent" track selected */
1100 tav->set_selected (true);
1101 tav->reshow_selection (selection->time);
1102
1103 } else {
1104
1105 /* possibly a child */
1106
1107 TimeAxisView::Children c = tav->get_child_list ();
1108
1109 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1110
1111 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1112
1113 if (control != (*i).controllable) {
1114 continue;
1115 }
1116
1117 (*j)->set_selected (true);
1118 (*j)->reshow_selection (selection->time);
1119 }
1120 }
1121
1122 stripables->push_back ((*i).stripable);
1123 }
1124
1125 ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1126 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1127 ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1128 ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1129 ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1130
1131 sensitize_the_right_region_actions (false);
1132
1133 /* STEP 4: notify control protocols */
1134
1135 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1136
1137 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1138 uint32_t audio_track_cnt = 0;
1139 uint32_t midi_track_cnt = 0;
1140
1141 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1142 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1143
1144 if (atv) {
1145 if (atv->is_audio_track()) {
1146 audio_track_cnt++;
1147 }
1148
1149 } else {
1150 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1151
1152 if (mtv) {
1153 if (mtv->is_midi_track()) {
1154 midi_track_cnt++;
1155 }
1156 }
1157 }
1158 }
1159
1160 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1161 }
1162 }
1163
1164 /* STEP 4: update EditorRoutes treeview */
1165
1166 PropertyChange soh;
1167
1168 soh.add (Properties::selected);
1169 soh.add (Properties::order);
1170 soh.add (Properties::hidden);
1171
1172 if (what_changed.contains (soh)) {
1173 _routes->sync_treeview_from_presentation_info (what_changed);
1174 }
1175 }
1176
1177 void
track_selection_changed()1178 Editor::track_selection_changed ()
1179 {
1180 /* reset paste count, so the plaste location doesn't get incremented
1181 * if we want to paste in the same place, but different track. */
1182 paste_count = 0;
1183
1184 if ( _session->solo_selection_active() )
1185 play_solo_selection(false);
1186 }
1187
1188 void
time_selection_changed()1189 Editor::time_selection_changed ()
1190 {
1191 /* XXX this is superficially inefficient. Hide the selection in all
1192 * tracks, then show it in all selected tracks.
1193 *
1194 * However, if you investigate what this actually does, it isn't
1195 * anywhere nearly as bad as it may appear. Remember: nothing is
1196 * redrawn or even recomputed during these two loops - that only
1197 * happens when we next render ...
1198 */
1199
1200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1201 (*i)->hide_selection ();
1202 }
1203
1204 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1205 (*i)->show_selection (selection->time);
1206 }
1207
1208 if (selection->time.empty()) {
1209 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1210 } else {
1211 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1212 }
1213
1214 /* propagate into backend, but only when there is no drag or we are at
1215 * the end of a drag, otherwise this is too expensive (could case a
1216 * locate per mouse motion event.
1217 */
1218
1219 if (_session && !_drags->active()) {
1220 if (selection->time.length() != 0) {
1221 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1222 } else {
1223 _session->clear_range_selection ();
1224 }
1225 }
1226 }
1227
1228 /** Set all region actions to have a given sensitivity */
1229 void
sensitize_all_region_actions(bool s)1230 Editor::sensitize_all_region_actions (bool s)
1231 {
1232 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1233
1234 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1235 (*i)->set_sensitive (s);
1236 }
1237
1238 _all_region_actions_sensitized = s;
1239 }
1240
1241 /** Sensitize region-based actions.
1242 *
1243 * This method is called from whenever we leave the canvas, either by moving
1244 * the pointer out of it, or by popping up a context menu. See
1245 * Editor::{entered,left}_track_canvas() for details there.
1246 */
1247 void
sensitize_the_right_region_actions(bool because_canvas_crossing)1248 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1249 {
1250 bool have_selection = false;
1251 bool have_entered = false;
1252 bool have_edit_point = false;
1253 bool have_selected_source = false;
1254 RegionSelection rs;
1255
1256 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1257 // << std::endl;
1258
1259 if (!selection->regions.empty()) {
1260 have_selection = true;
1261 rs = selection->regions;
1262 }
1263
1264 if (entered_regionview) {
1265 have_entered = true;
1266 rs.add (entered_regionview);
1267 }
1268
1269 if ( _sources->get_single_selection() ) {
1270 have_selected_source = true;
1271 }
1272
1273 if (rs.empty() && !selection->tracks.empty()) {
1274
1275 /* no selected regions, but some selected tracks.
1276 */
1277
1278 if (_edit_point == EditAtMouse) {
1279 if (!within_track_canvas) {
1280 /* pointer is not in canvas, so edit point is meaningless */
1281 have_edit_point = false;
1282 } else {
1283 /* inside canvas. we don't know where the edit
1284 point will be when an action is invoked, but
1285 assume it could intersect with a region.
1286 */
1287 have_edit_point = true;
1288 }
1289 } else {
1290 RegionSelection at_edit_point;
1291 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1292 get_regions_at (at_edit_point, where, selection->tracks);
1293 if (!at_edit_point.empty()) {
1294 have_edit_point = true;
1295 }
1296 if (rs.empty()) {
1297 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1298 }
1299 }
1300 }
1301
1302 //std::cerr << "\tfinal have selection: " << have_selection
1303 // << " have entered " << have_entered
1304 // << " have edit point " << have_edit_point
1305 // << " EP = " << enum_2_string (_edit_point)
1306 // << std::endl;
1307
1308 typedef std::map<std::string,RegionAction> RegionActionMap;
1309
1310 _ignore_region_action = true;
1311
1312 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1313 RegionActionTarget tgt = x->second.target;
1314 bool sensitive = false;
1315
1316 if ((tgt & SelectedRegions) && have_selection) {
1317 sensitive = true;
1318 } else if ((tgt & EnteredRegions) && have_entered) {
1319 sensitive = true;
1320 } else if ((tgt & EditPointRegions) && have_edit_point) {
1321 sensitive = true;
1322 } else if ((tgt & ListSelection) && have_selected_source ) {
1323 sensitive = true;
1324 }
1325
1326 x->second.action->set_sensitive (sensitive);
1327 }
1328
1329 /* Look through the regions that are selected and make notes about what we have got */
1330
1331 bool have_audio = false;
1332 bool have_multichannel_audio = false;
1333 bool have_midi = false;
1334 bool have_locked = false;
1335 bool have_unlocked = false;
1336 bool have_video_locked = false;
1337 bool have_video_unlocked = false;
1338 bool have_position_lock_style_audio = false;
1339 bool have_position_lock_style_music = false;
1340 bool have_muted = false;
1341 bool have_unmuted = false;
1342 bool have_opaque = false;
1343 bool have_non_opaque = false;
1344 bool have_not_at_natural_position = false;
1345 bool have_envelope_active = false;
1346 bool have_envelope_inactive = false;
1347 bool have_non_unity_scale_amplitude = false;
1348 bool have_compound_regions = false;
1349 bool have_inactive_fade_in = false;
1350 bool have_inactive_fade_out = false;
1351 bool have_active_fade_in = false;
1352 bool have_active_fade_out = false;
1353 bool have_transients = false;
1354
1355 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1356
1357 boost::shared_ptr<Region> r = (*i)->region ();
1358 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1359
1360 if (ar) {
1361 have_audio = true;
1362 if (ar->n_channels() > 1) {
1363 have_multichannel_audio = true;
1364 }
1365 }
1366
1367 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1368 have_midi = true;
1369 }
1370
1371 if (r->is_compound()) {
1372 have_compound_regions = true;
1373 }
1374
1375 if (r->locked()) {
1376 have_locked = true;
1377 } else {
1378 have_unlocked = true;
1379 }
1380
1381 if (r->video_locked()) {
1382 have_video_locked = true;
1383 } else {
1384 have_video_unlocked = true;
1385 }
1386
1387 if (r->position_lock_style() == MusicTime) {
1388 have_position_lock_style_music = true;
1389 } else {
1390 have_position_lock_style_audio = true;
1391 }
1392
1393 if (r->muted()) {
1394 have_muted = true;
1395 } else {
1396 have_unmuted = true;
1397 }
1398
1399 if (r->opaque()) {
1400 have_opaque = true;
1401 } else {
1402 have_non_opaque = true;
1403 }
1404
1405 if (!r->at_natural_position()) {
1406 have_not_at_natural_position = true;
1407 }
1408
1409 if (r->has_transients ()){
1410 have_transients = true;
1411 }
1412
1413 if (ar) {
1414 if (ar->envelope_active()) {
1415 have_envelope_active = true;
1416 } else {
1417 have_envelope_inactive = true;
1418 }
1419
1420 if (ar->scale_amplitude() != 1) {
1421 have_non_unity_scale_amplitude = true;
1422 }
1423
1424 if (ar->fade_in_active ()) {
1425 have_active_fade_in = true;
1426 } else {
1427 have_inactive_fade_in = true;
1428 }
1429
1430 if (ar->fade_out_active ()) {
1431 have_active_fade_out = true;
1432 } else {
1433 have_inactive_fade_out = true;
1434 }
1435 }
1436 }
1437
1438 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1439
1440 if (rs.size() > 1) {
1441 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1442 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1443 _region_actions->get_action("rename-region")->set_sensitive (false);
1444 if (have_audio) {
1445 /* XXX need to check whether there is than 1 per
1446 playlist, because otherwise this makes no sense.
1447 */
1448 _region_actions->get_action("combine-regions")->set_sensitive (true);
1449 } else {
1450 _region_actions->get_action("combine-regions")->set_sensitive (false);
1451 }
1452 } else if (rs.size() == 1) {
1453 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1454 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1455 _region_actions->get_action("combine-regions")->set_sensitive (false);
1456 }
1457
1458 if (!have_multichannel_audio) {
1459 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1460 }
1461
1462 if (!have_midi) {
1463 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1464 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1465 _region_actions->get_action("quantize-region")->set_sensitive (false);
1466 _region_actions->get_action("legatize-region")->set_sensitive (false);
1467 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1468 _region_actions->get_action("transform-region")->set_sensitive (false);
1469 _region_actions->get_action("fork-region")->set_sensitive (false);
1470 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1471 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1472 _region_actions->get_action("transpose-region")->set_sensitive (false);
1473 } else {
1474 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1475 /* others were already marked sensitive */
1476 }
1477
1478 /* ok, moving along... */
1479
1480 if (have_compound_regions) {
1481 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1482 } else {
1483 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1484 }
1485
1486 if (have_audio) {
1487
1488 if (have_envelope_active && !have_envelope_inactive) {
1489 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1490 } else if (have_envelope_active && have_envelope_inactive) {
1491 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1492 }
1493
1494 } else {
1495
1496 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1497 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1498 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1499 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1500 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1501 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1502 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1503
1504 }
1505
1506 if (!have_non_unity_scale_amplitude || !have_audio) {
1507 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1508 }
1509
1510 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1511 a->set_active (have_locked && !have_unlocked);
1512 if (have_locked && have_unlocked) {
1513 // a->set_inconsistent ();
1514 }
1515
1516 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1517 a->set_active (have_video_locked && !have_video_unlocked);
1518 if (have_video_locked && have_video_unlocked) {
1519 // a->set_inconsistent ();
1520 }
1521
1522 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1523 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1524
1525 vector<Widget*> proxies = a->get_proxies();
1526 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1527 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1528 if (cmi) {
1529 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1530 }
1531 }
1532
1533 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1534 a->set_active (have_muted && !have_unmuted);
1535 if (have_muted && have_unmuted) {
1536 // a->set_inconsistent ();
1537 }
1538
1539 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1540 a->set_active (have_opaque && !have_non_opaque);
1541 if (have_opaque && have_non_opaque) {
1542 // a->set_inconsistent ();
1543 }
1544
1545 if (!have_not_at_natural_position) {
1546 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1547 }
1548
1549 /* Todo: insert-region-from-source-list */
1550 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1551 #if 0
1552 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1553 _region_actions->get_action("insert-region-from-source-list")->set_sensitive (false);
1554 } else {
1555 _region_actions->get_action("insert-region-from-source-list")->set_sensitive (true);
1556 }
1557 #endif
1558
1559 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1560 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1561 if (have_active_fade_in && have_inactive_fade_in) {
1562 // a->set_inconsistent ();
1563 }
1564
1565 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1566 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1567
1568 if (have_active_fade_out && have_inactive_fade_out) {
1569 // a->set_inconsistent ();
1570 }
1571
1572 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1573 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1574
1575 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1576 a->set_active (have_active_fade && !have_inactive_fade);
1577
1578 if (have_active_fade && have_inactive_fade) {
1579 // a->set_inconsistent ();
1580 }
1581
1582 _ignore_region_action = false;
1583
1584 _all_region_actions_sensitized = false;
1585 }
1586
1587 void
region_selection_changed()1588 Editor::region_selection_changed ()
1589 {
1590 _regions->block_change_connection (true);
1591 editor_regions_selection_changed_connection.block(true);
1592
1593 if (_region_selection_change_updates_region_list) {
1594 _regions->unselect_all ();
1595 }
1596
1597 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1598 (*i)->set_selected_regionviews (selection->regions);
1599 }
1600
1601 if (_region_selection_change_updates_region_list) {
1602 _regions->set_selected (selection->regions);
1603 }
1604
1605 _regions->block_change_connection (false);
1606 editor_regions_selection_changed_connection.block(false);
1607
1608 sensitize_the_right_region_actions (false);
1609
1610 /* propagate into backend */
1611 assert (_session);
1612
1613 if (!selection->regions.empty()) {
1614 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1615 } else {
1616 _session->clear_object_selection ();
1617 }
1618
1619 if (_session->solo_selection_active()) {
1620 play_solo_selection(false);
1621 }
1622
1623 /* set nudge button color */
1624 if (! get_regions_from_selection_and_entered().empty()) {
1625 /* nudge regions */
1626 nudge_forward_button.set_name ("nudge button");
1627 nudge_backward_button.set_name ("nudge button");
1628 } else {
1629 /* nudge marker or playhead */
1630 nudge_forward_button.set_name ("transport button");
1631 nudge_backward_button.set_name ("transport button");
1632 }
1633
1634 //there are a few global Editor->Select actions which select regions even if you aren't in Object mode.
1635 //if regions are selected, we must always force the mouse mode to Object...
1636 //... otherwise the user is confusingly left with selected regions that can't be manipulated.
1637 if (!selection->regions.empty() && !internal_editing()) {
1638
1639 /* if in MouseAudition and there's just 1 region selected
1640 * (i.e. we just clicked on it), leave things as they are
1641 */
1642
1643 if (selection->regions.size() > 1 || mouse_mode != Editing::MouseAudition) {
1644 set_mouse_mode (MouseObject, false);
1645 }
1646 }
1647 }
1648
1649 void
point_selection_changed()1650 Editor::point_selection_changed ()
1651 {
1652 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1653 (*i)->set_selected_points (selection->points);
1654 }
1655 }
1656
1657 void
select_all_in_track(Selection::Operation op)1658 Editor::select_all_in_track (Selection::Operation op)
1659 {
1660 list<Selectable *> touched;
1661
1662 if (!clicked_routeview) {
1663 return;
1664 }
1665
1666 begin_reversible_selection_op (X_("Select All in Track"));
1667
1668 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1669
1670 switch (op) {
1671 case Selection::Toggle:
1672 selection->add (touched);
1673 break;
1674 case Selection::Set:
1675 selection->set (touched);
1676 break;
1677 case Selection::Extend:
1678 /* meaningless, because we're selecting everything */
1679 break;
1680 case Selection::Add:
1681 selection->add (touched);
1682 break;
1683 }
1684
1685 commit_reversible_selection_op ();
1686 }
1687
1688 bool
select_all_internal_edit(Selection::Operation)1689 Editor::select_all_internal_edit (Selection::Operation)
1690 {
1691 bool selected = false;
1692
1693 RegionSelection copy (selection->regions);
1694
1695 for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) {
1696 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1697 if (mrv) {
1698 mrv->select_all_notes ();
1699 selected = true;
1700 }
1701 }
1702
1703 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1704 if (mrv) {
1705 mrv->select_all_notes ();
1706 selected = true;
1707 }
1708
1709 return selected;
1710 }
1711
1712 void
select_all_objects(Selection::Operation op)1713 Editor::select_all_objects (Selection::Operation op)
1714 {
1715 list<Selectable *> touched;
1716
1717 if (internal_editing() && select_all_internal_edit(op)) {
1718 return; // Selected notes
1719 }
1720
1721 TrackViewList ts;
1722
1723 if (selection->tracks.empty()) {
1724 ts = track_views;
1725 } else {
1726 ts = selection->tracks;
1727 }
1728
1729 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1730 if ((*iter)->hidden()) {
1731 continue;
1732 }
1733 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1734 }
1735
1736 begin_reversible_selection_op (X_("select all"));
1737 switch (op) {
1738 case Selection::Add:
1739 selection->add (touched);
1740 break;
1741 case Selection::Toggle:
1742 selection->toggle (touched);
1743 break;
1744 case Selection::Set:
1745 selection->set (touched);
1746 break;
1747 case Selection::Extend:
1748 /* meaningless, because we're selecting everything */
1749 break;
1750 }
1751 commit_reversible_selection_op ();
1752 }
1753
1754 void
invert_selection_in_track()1755 Editor::invert_selection_in_track ()
1756 {
1757 list<Selectable *> touched;
1758
1759 if (!clicked_routeview) {
1760 return;
1761 }
1762
1763 begin_reversible_selection_op (X_("Invert Selection in Track"));
1764 clicked_routeview->get_inverted_selectables (*selection, touched);
1765 selection->set (touched);
1766 commit_reversible_selection_op ();
1767 }
1768
1769 void
invert_selection()1770 Editor::invert_selection ()
1771 {
1772
1773 if (internal_editing()) {
1774 MidiRegionSelection ms = selection->midi_regions();
1775 for (MidiRegionSelection::iterator i = ms.begin(); i != ms.end(); ++i) {
1776 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1777 if (mrv) {
1778 mrv->invert_selection ();
1779 }
1780 }
1781 return;
1782 }
1783
1784 if (!selection->tracks.empty()) {
1785
1786 TrackViewList inverted;
1787
1788 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1789 if (!(*iter)->selected()) {
1790 inverted.push_back (*iter);
1791 }
1792 }
1793
1794 begin_reversible_selection_op (X_("Invert Track Selection"));
1795 selection->set (inverted);
1796 commit_reversible_selection_op ();
1797
1798 } else {
1799
1800 list<Selectable *> touched;
1801
1802 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1803 if ((*iter)->hidden()) {
1804 continue;
1805 }
1806 (*iter)->get_inverted_selectables (*selection, touched);
1807 }
1808
1809 begin_reversible_selection_op (X_("Invert ObjectSelection"));
1810 selection->set (touched);
1811 commit_reversible_selection_op ();
1812 }
1813 }
1814
1815 /** @param start Start time in session samples.
1816 * @param end End time in session samples.
1817 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1818 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1819 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1820 * within the region are already selected.
1821 */
1822 void
select_all_within(samplepos_t start,samplepos_t end,double top,double bot,const TrackViewList & tracklist,Selection::Operation op,bool preserve_if_selected)1823 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1824 {
1825 list<Selectable*> found;
1826
1827 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1828
1829 if ((*iter)->hidden()) {
1830 continue;
1831 }
1832
1833 (*iter)->get_selectables (start, end, top, bot, found);
1834 }
1835
1836 if (found.empty()) {
1837 selection->clear_objects();
1838 selection->clear_time ();
1839 return;
1840 }
1841
1842 if (preserve_if_selected && op != Selection::Toggle) {
1843 list<Selectable*>::iterator i = found.begin();
1844 while (i != found.end() && (*i)->selected()) {
1845 ++i;
1846 }
1847
1848 if (i == found.end()) {
1849 return;
1850 }
1851 }
1852
1853 begin_reversible_selection_op (X_("select all within"));
1854 switch (op) {
1855 case Selection::Add:
1856 selection->add (found);
1857 break;
1858 case Selection::Toggle:
1859 selection->toggle (found);
1860 break;
1861 case Selection::Set:
1862 selection->set (found);
1863 break;
1864 case Selection::Extend:
1865 /* not defined yet */
1866 break;
1867 }
1868
1869 commit_reversible_selection_op ();
1870 }
1871
1872 void
set_selection_from_region()1873 Editor::set_selection_from_region ()
1874 {
1875 if (selection->regions.empty()) {
1876 return;
1877 }
1878
1879 /* find all the tracks that have selected regions */
1880
1881 set<TimeAxisView*> tracks;
1882
1883 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1884 tracks.insert (&(*r)->get_time_axis_view());
1885 }
1886
1887 TrackViewList tvl;
1888 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1889
1890 /* select range (this will clear the region selection) */
1891
1892 selection->set (selection->regions.start(), selection->regions.end_sample());
1893
1894 /* and select the tracks */
1895
1896 selection->set (tvl);
1897
1898 if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1899 set_mouse_mode (Editing::MouseRange, false);
1900 }
1901 }
1902
1903 void
set_selection_from_punch()1904 Editor::set_selection_from_punch()
1905 {
1906 Location* location;
1907
1908 if ((location = _session->locations()->auto_punch_location()) == 0) {
1909 return;
1910 }
1911
1912 set_selection_from_range (*location);
1913 }
1914
1915 void
set_selection_from_loop()1916 Editor::set_selection_from_loop()
1917 {
1918 Location* location;
1919
1920 if ((location = _session->locations()->auto_loop_location()) == 0) {
1921 return;
1922 }
1923 set_selection_from_range (*location);
1924 }
1925
1926 void
set_selection_from_range(Location & loc)1927 Editor::set_selection_from_range (Location& loc)
1928 {
1929 begin_reversible_selection_op (X_("set selection from range"));
1930
1931 selection->set (loc.start(), loc.end());
1932
1933 // if no tracks are selected, enable all tracks
1934 // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1935 if (selection->tracks.empty()) {
1936 select_all_visible_lanes();
1937 }
1938
1939 commit_reversible_selection_op ();
1940
1941 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1942 set_mouse_mode (MouseRange, false);
1943 }
1944 }
1945
1946 void
select_all_selectables_using_time_selection()1947 Editor::select_all_selectables_using_time_selection ()
1948 {
1949 list<Selectable *> touched;
1950
1951 if (selection->time.empty()) {
1952 return;
1953 }
1954
1955 samplepos_t start = selection->time[clicked_selection].start;
1956 samplepos_t end = selection->time[clicked_selection].end;
1957
1958 if (end - start < 1) {
1959 return;
1960 }
1961
1962 TrackViewList* ts;
1963
1964 if (selection->tracks.empty()) {
1965 ts = &track_views;
1966 } else {
1967 ts = &selection->tracks;
1968 }
1969
1970 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1971 if ((*iter)->hidden()) {
1972 continue;
1973 }
1974 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1975 }
1976
1977 begin_reversible_selection_op (X_("select all from range"));
1978 selection->set (touched);
1979 commit_reversible_selection_op ();
1980 }
1981
1982
1983 void
select_all_selectables_using_punch()1984 Editor::select_all_selectables_using_punch()
1985 {
1986 Location* location = _session->locations()->auto_punch_location();
1987 list<Selectable *> touched;
1988
1989 if (location == 0 || (location->end() - location->start() <= 1)) {
1990 return;
1991 }
1992
1993
1994 TrackViewList* ts;
1995
1996 if (selection->tracks.empty()) {
1997 ts = &track_views;
1998 } else {
1999 ts = &selection->tracks;
2000 }
2001
2002 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2003 if ((*iter)->hidden()) {
2004 continue;
2005 }
2006 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
2007 }
2008 begin_reversible_selection_op (X_("select all from punch"));
2009 selection->set (touched);
2010 commit_reversible_selection_op ();
2011
2012 }
2013
2014 void
select_all_selectables_using_loop()2015 Editor::select_all_selectables_using_loop()
2016 {
2017 Location* location = _session->locations()->auto_loop_location();
2018 list<Selectable *> touched;
2019
2020 if (location == 0 || (location->end() - location->start() <= 1)) {
2021 return;
2022 }
2023
2024
2025 TrackViewList* ts;
2026
2027 if (selection->tracks.empty()) {
2028 ts = &track_views;
2029 } else {
2030 ts = &selection->tracks;
2031 }
2032
2033 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2034 if ((*iter)->hidden()) {
2035 continue;
2036 }
2037 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
2038 }
2039 begin_reversible_selection_op (X_("select all from loop"));
2040 selection->set (touched);
2041 commit_reversible_selection_op ();
2042
2043 }
2044
2045 void
select_all_selectables_using_cursor(EditorCursor * cursor,bool after)2046 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
2047 {
2048 samplepos_t start;
2049 samplepos_t end;
2050 list<Selectable *> touched;
2051
2052 if (after) {
2053 start = cursor->current_sample();
2054 end = _session->current_end_sample();
2055 } else {
2056 if (cursor->current_sample() > 0) {
2057 start = 0;
2058 end = cursor->current_sample() - 1;
2059 } else {
2060 return;
2061 }
2062 }
2063
2064 if (internal_editing()) {
2065 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2066 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2067 if (mrv) {
2068 mrv->select_range (start, end);
2069 }
2070 }
2071 return;
2072 }
2073
2074 if (after) {
2075 begin_reversible_selection_op (X_("select all after cursor"));
2076 } else {
2077 begin_reversible_selection_op (X_("select all before cursor"));
2078 }
2079
2080 TrackViewList* ts;
2081
2082 if (selection->tracks.empty()) {
2083 ts = &track_views;
2084 } else {
2085 ts = &selection->tracks;
2086 }
2087
2088 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2089 if ((*iter)->hidden()) {
2090 continue;
2091 }
2092 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2093 }
2094 selection->set (touched);
2095 commit_reversible_selection_op ();
2096 }
2097
2098 void
select_all_selectables_using_edit(bool after,bool from_context_menu)2099 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2100 {
2101 samplepos_t start;
2102 samplepos_t end;
2103 list<Selectable *> touched;
2104
2105 if (after) {
2106 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2107 end = _session->current_end_sample();
2108 } else {
2109 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2110 start = 0;
2111 end -= 1;
2112 } else {
2113 return;
2114 }
2115 }
2116
2117 if (internal_editing()) {
2118 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2119 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2120 mrv->select_range (start, end);
2121 }
2122 return;
2123 }
2124
2125 if (after) {
2126 begin_reversible_selection_op (X_("select all after edit"));
2127 } else {
2128 begin_reversible_selection_op (X_("select all before edit"));
2129 }
2130
2131 TrackViewList* ts;
2132
2133 if (selection->tracks.empty()) {
2134 ts = &track_views;
2135 } else {
2136 ts = &selection->tracks;
2137 }
2138
2139 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2140 if ((*iter)->hidden()) {
2141 continue;
2142 }
2143 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2144 }
2145 selection->set (touched);
2146 commit_reversible_selection_op ();
2147 }
2148
2149 void
select_all_selectables_between(bool within)2150 Editor::select_all_selectables_between (bool within)
2151 {
2152 samplepos_t start;
2153 samplepos_t end;
2154 list<Selectable *> touched;
2155
2156 if (!get_edit_op_range (start, end)) {
2157 return;
2158 }
2159
2160 if (internal_editing()) {
2161 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2162 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2163 mrv->select_range (start, end);
2164 }
2165 return;
2166 }
2167
2168 TrackViewList* ts;
2169
2170 if (selection->tracks.empty()) {
2171 ts = &track_views;
2172 } else {
2173 ts = &selection->tracks;
2174 }
2175
2176 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2177 if ((*iter)->hidden()) {
2178 continue;
2179 }
2180 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2181 }
2182
2183 begin_reversible_selection_op (X_("Select all Selectables Between"));
2184 selection->set (touched);
2185 commit_reversible_selection_op ();
2186 }
2187
2188 void
select_range_between()2189 Editor::select_range_between ()
2190 {
2191 samplepos_t start;
2192 samplepos_t end;
2193
2194 if (!selection->time.empty()) {
2195 selection->clear_time ();
2196 }
2197
2198 if (!get_edit_op_range (start, end)) {
2199 return;
2200 }
2201
2202 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2203 set_mouse_mode (MouseRange, false);
2204 }
2205
2206 begin_reversible_selection_op (X_("Select Range Between"));
2207 selection->set (start, end);
2208 commit_reversible_selection_op ();
2209 }
2210
2211 bool
get_edit_op_range(samplepos_t & start,samplepos_t & end) const2212 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2213 {
2214 /* if an explicit range exists, use it */
2215
2216 if ((mouse_mode == MouseRange || get_smart_mode()) && !selection->time.empty()) {
2217 /* we know that these are ordered */
2218 start = selection->time.start();
2219 end = selection->time.end_sample();
2220 return true;
2221 } else {
2222 start = 0;
2223 end = 0;
2224 return false;
2225 }
2226 }
2227
2228 void
deselect_all()2229 Editor::deselect_all ()
2230 {
2231 begin_reversible_selection_op (X_("Deselect All"));
2232 selection->clear ();
2233 commit_reversible_selection_op ();
2234 }
2235
2236 long
select_range(samplepos_t s,samplepos_t e)2237 Editor::select_range (samplepos_t s, samplepos_t e)
2238 {
2239 begin_reversible_selection_op (X_("Select Range"));
2240 selection->add (clicked_axisview);
2241 selection->time.clear ();
2242 long ret = selection->set (s, e);
2243 commit_reversible_selection_op ();
2244 return ret;
2245 }
2246
2247 void
catch_up_on_midi_selection()2248 Editor::catch_up_on_midi_selection ()
2249 {
2250 RegionSelection regions;
2251
2252 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2253 if ((*iter)->hidden()) {
2254 continue;
2255 }
2256
2257 MidiTimeAxisView* matv = dynamic_cast<MidiTimeAxisView*> (*iter);
2258 if (!matv) {
2259 continue;
2260 }
2261
2262 matv->get_regions_with_selected_data (regions);
2263 }
2264
2265 if (!regions.empty()) {
2266 selection->set (regions);
2267 }
2268 }
2269