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