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