Patch from Lincoln to fix uncommitted reversible command during rubberband selection...
[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
25 #include "ardour/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29 #include "ardour/midi_region.h"
30
31 #include "editor.h"
32 #include "actions.h"
33 #include "audio_time_axis.h"
34 #include "audio_region_view.h"
35 #include "audio_streamview.h"
36 #include "automation_line.h"
37 #include "control_point.h"
38 #include "editor_regions.h"
39 #include "editor_cursors.h"
40
41 #include "i18n.h"
42
43 using namespace std;
44 using namespace ARDOUR;
45 using namespace PBD;
46 using namespace Gtk;
47 using namespace Glib;
48 using namespace Gtkmm2ext;
49 using namespace Editing;
50
51 struct TrackViewByPositionSorter
52 {
53     bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
54             return a->y_position() < b->y_position();
55     }
56 };
57
58 bool
59 Editor::extend_selection_to_track (TimeAxisView& view)
60 {
61         if (selection->selected (&view)) {
62                 /* already selected, do nothing */
63                 return false;
64         }
65
66         if (selection->tracks.empty()) {
67
68                 if (!selection->selected (&view)) {
69                         selection->set (&view);
70                         return true;
71                 } else {
72                         return false;
73                 }
74         }
75
76         /* something is already selected, so figure out which range of things to add */
77
78         TrackViewList to_be_added;
79         TrackViewList sorted = track_views;
80         TrackViewByPositionSorter cmp;
81         bool passed_clicked = false;
82         bool forwards = true;
83
84         sorted.sort (cmp);
85
86         if (!selection->selected (&view)) {
87                 to_be_added.push_back (&view);
88         }
89
90         /* figure out if we should go forward or backwards */
91
92         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
93
94                 if ((*i) == &view) {
95                         passed_clicked = true;
96                 }
97
98                 if (selection->selected (*i)) {
99                         if (passed_clicked) {
100                                 forwards = true;
101                         } else {
102                                 forwards = false;
103                         }
104                         break;
105                 }
106         }
107
108         passed_clicked = false;
109
110         if (forwards) {
111
112                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
113
114                         if ((*i) == &view) {
115                                 passed_clicked = true;
116                                 continue;
117                         }
118
119                         if (passed_clicked) {
120                                 if ((*i)->hidden()) {
121                                         continue;
122                                 }
123                                 if (selection->selected (*i)) {
124                                         break;
125                                 } else if (!(*i)->hidden()) {
126                                         to_be_added.push_back (*i);
127                                 }
128                         }
129                 }
130
131         } else {
132
133                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
134
135                         if ((*r) == &view) {
136                                 passed_clicked = true;
137                                 continue;
138                         }
139
140                         if (passed_clicked) {
141
142                                 if ((*r)->hidden()) {
143                                         continue;
144                                 }
145
146                                 if (selection->selected (*r)) {
147                                         break;
148                                 } else if (!(*r)->hidden()) {
149                                         to_be_added.push_back (*r);
150                                 }
151                         }
152                 }
153         }
154
155         if (!to_be_added.empty()) {
156                 selection->add (to_be_added);
157                 return true;
158         }
159
160         return false;
161 }
162
163 void
164 Editor::select_all_tracks ()
165 {
166         TrackViewList visible_views;
167         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
168                 if ((*i)->marked_for_display()) {
169                         visible_views.push_back (*i);
170                 }
171         }
172         selection->set (visible_views);
173 }
174
175 /** Select clicked_axisview, unless there are no currently selected
176  *  tracks, in which case nothing will happen unless `force' is true.
177  */
178 void
179 Editor::set_selected_track_as_side_effect (Selection::Operation op, bool /*force*/)
180 {
181         if (!clicked_axisview) {
182                 return;
183         }
184
185 #if 1
186         if (!clicked_routeview) {
187                 return;
188         }
189
190         bool had_tracks = !selection->tracks.empty();
191         RouteGroup* group = clicked_routeview->route()->route_group();
192         RouteGroup& arg (_session->all_route_group());
193         
194         switch (op) {
195         case Selection::Toggle: 
196                 if (selection->selected (clicked_axisview)) {
197                         if (arg.is_select() && arg.is_active()) {
198                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
199                                         selection->remove(*i);
200                                 }
201                         } else if (group && group->is_active()) {
202                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203                                         if ((*i)->route_group() == group)
204                                                 selection->remove(*i);
205                                 }
206                         } else {
207                                 selection->remove (clicked_axisview);
208                         }
209                 } else {
210                         if (arg.is_select() && arg.is_active()) {
211                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
212                                                 selection->add(*i);
213                                 }
214                         } else if (group && group->is_active()) {
215                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216                                         if ( (*i)->route_group() == group)
217                                                 selection->add(*i);
218                                 }
219                         } else {
220                                 selection->add (clicked_axisview);
221                         }
222                 }
223                 break;
224         
225         case Selection::Add: 
226                 if (!had_tracks && arg.is_select() && arg.is_active()) {
227                         /* nothing was selected already, and all group is active etc. so use
228                            all tracks.
229                         */
230                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
231                                         selection->add(*i);
232                         }
233                 } else if (group && group->is_active()) {
234                         for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
235                                 if ((*i)->route_group() == group)
236                                         selection->add(*i);
237                         }
238                 } else {
239                         selection->add (clicked_axisview);
240                 }
241                 break;
242                 
243         case Selection::Set:
244                 selection->clear();
245                 if (!had_tracks && arg.is_select() && arg.is_active()) {
246                         /* nothing was selected already, and all group is active etc. so use
247                            all tracks.
248                         */
249                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
250                                         selection->add(*i);
251                         }
252                 } else if (group && group->is_active()) {
253                         for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
254                                 if ((*i)->route_group() == group)
255                                         selection->add(*i);
256                         }
257                 } else {
258                         selection->set (clicked_axisview);
259                 }
260                 break;
261         
262         case Selection::Extend: 
263                 selection->clear();
264                 cerr << ("Editor::set_selected_track_as_side_effect  case  Selection::Add  not yet implemented\n");
265                 break;
266         }
267
268 #else // the older version
269
270         if (!selection->tracks.empty()) {
271                 if (!selection->selected (clicked_axisview)) {
272                         selection->add (clicked_axisview);
273                 }
274
275         } else if (force) {
276                 selection->set (clicked_axisview);
277         }
278 #endif
279 }
280
281 void
282 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
283 {
284         switch (op) {
285         case Selection::Toggle:
286                 if (selection->selected (&view)) {
287                         if (!no_remove) {
288                                 selection->remove (&view);
289                         }
290                 } else {
291                         selection->add (&view);
292                 }
293                 break;
294
295         case Selection::Add:
296                 if (!selection->selected (&view)) {
297                         selection->add (&view);
298                 }
299                 break;
300
301         case Selection::Set:
302                 selection->set (&view);
303                 break;
304
305         case Selection::Extend:
306                 extend_selection_to_track (view);
307                 break;
308         }
309 }
310
311 void
312 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
313 {
314         if (!clicked_routeview) {
315                 return;
316         }
317
318         if (!press) {
319                 return;
320         }
321
322         set_selected_track (*clicked_routeview, op, no_remove);
323 }
324
325 bool
326 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
327 {
328         if (!clicked_control_point) {
329                 return false;
330         }
331         
332         switch (op) {
333         case Selection::Set:
334                 selection->set (clicked_control_point);
335                 break;
336         case Selection::Add:
337                 selection->add (clicked_control_point);
338                 break;
339         case Selection::Toggle:
340                 selection->toggle (clicked_control_point);
341                 break;
342         case Selection::Extend:
343                 /* XXX */
344                 break;
345         }
346
347         return true;
348 }
349
350 void
351 Editor::get_onscreen_tracks (TrackViewList& tvl)
352 {
353         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
354                 if ((*i)->y_position() < _canvas_height) {
355                         tvl.push_back (*i);
356                 }
357         }
358 }
359
360 /** Call a slot for a given `basis' track and also for any track that is in the same
361  *  active route group with a particular set of properties.
362  *
363  *  @param sl Slot to call.
364  *  @param basis Basis track.
365  *  @param prop Properties that active edit groups must share to be included in the map.
366  */
367
368 void
369 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
370 {
371         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
372
373         if (route_basis == 0) {
374                 return;
375         }
376
377         set<RouteTimeAxisView*> tracks;
378         tracks.insert (route_basis);
379
380         RouteGroup* group = route_basis->route()->route_group();
381
382         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
383
384                 /* the basis is a member of an active route group, with the appropriate
385                    properties; find other members */
386
387                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
388                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
389                         if (v && v->route()->route_group() == group) {
390                                 tracks.insert (v);
391                         }
392                 }
393         }
394
395         /* call the slots */
396         uint32_t const sz = tracks.size ();
397         
398         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
399                 sl (**i, sz);
400         }
401 }
402
403 void
404 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
405 {
406         boost::shared_ptr<Playlist> pl;
407         vector<boost::shared_ptr<Region> > results;
408         RegionView* marv;
409         boost::shared_ptr<Track> tr;
410
411         if ((tr = tv.track()) == 0) {
412                 /* bus */
413                 return;
414         }
415
416         if (&tv == &basis->get_time_axis_view()) {
417                 /* looking in same track as the original */
418                 return;
419         }
420
421         if ((pl = tr->playlist()) != 0) {
422                 pl->get_equivalent_regions (basis->region(), results);
423         }
424
425         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
426                 if ((marv = tv.view()->find_view (*ir)) != 0) {
427                         all_equivs->push_back (marv);
428                 }
429         }
430 }
431
432 void
433 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
434 {
435         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
436
437         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
438
439         equivalent_regions.push_back (basis);
440 }
441
442 RegionSelection
443 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
444 {
445         RegionSelection equivalent;
446
447         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
448
449                 vector<RegionView*> eq;
450
451                 mapover_tracks (
452                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
453                         &(*i)->get_time_axis_view(), prop
454                         );
455
456                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
457                         equivalent.add (*j);
458                 }
459
460                 equivalent.add (*i);
461         }
462
463         return equivalent;
464 }
465
466
467 int
468 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
469 {
470         int region_count = 0;
471
472         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
473
474                 RouteTimeAxisView* tatv;
475
476                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
477
478                         boost::shared_ptr<Playlist> pl;
479                         vector<boost::shared_ptr<Region> > results;
480                         RegionView* marv;
481                         boost::shared_ptr<Track> tr;
482
483                         if ((tr = tatv->track()) == 0) {
484                                 /* bus */
485                                 continue;
486                         }
487
488                         if ((pl = (tr->playlist())) != 0) {
489                                 pl->get_region_list_equivalent_regions (region, results);
490                         }
491
492                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
493                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
494                                         region_count++;
495                                 }
496                         }
497
498                 }
499         }
500
501         return region_count;
502 }
503
504
505 bool
506 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
507 {
508         vector<RegionView*> all_equivalent_regions;
509         bool commit = false;
510
511         if (!clicked_regionview || !clicked_routeview) {
512                 return false;
513         }
514
515         if (press) {
516                 button_release_can_deselect = false;
517         }
518
519         if (op == Selection::Toggle || op == Selection::Set) {
520
521
522                 switch (op) {
523                 case Selection::Toggle:
524
525                         if (selection->selected (clicked_regionview)) {
526                                 if (press) {
527
528                                         /* whatever was clicked was selected already; do nothing here but allow
529                                            the button release to deselect it
530                                         */
531
532                                         button_release_can_deselect = true;
533
534                                 } else {
535
536                                         if (button_release_can_deselect) {
537
538                                                 /* just remove this one region, but only on a permitted button release */
539
540                                                 selection->remove (clicked_regionview);
541                                                 commit = true;
542
543                                                 /* no more deselect action on button release till a new press
544                                                    finds an already selected object.
545                                                 */
546
547                                                 button_release_can_deselect = false;
548                                         }
549                                 }
550
551                         } else {
552
553                                 if (press) {
554
555                                         if (selection->selected (clicked_routeview)) {
556                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
557                                         } else {
558                                                 all_equivalent_regions.push_back (clicked_regionview);
559                                         }
560
561                                         /* add all the equivalent regions, but only on button press */
562
563                                         if (!all_equivalent_regions.empty()) {
564                                                 commit = true;
565                                         }
566
567                                         selection->add (all_equivalent_regions);
568                                 }
569                         }
570                         break;
571
572                 case Selection::Set:
573                         if (!selection->selected (clicked_regionview)) {
574                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
575                                 selection->set (all_equivalent_regions);
576                                 commit = true;
577                         } else {
578                                 /* no commit necessary: clicked on an already selected region */
579                                 goto out;
580                         }
581                         break;
582
583                 default:
584                         /* silly compiler */
585                         break;
586                 }
587
588         } else if (op == Selection::Extend) {
589
590                 list<Selectable*> results;
591                 framepos_t last_frame;
592                 framepos_t first_frame;
593                 bool same_track = false;
594
595                 /* 1. find the last selected regionview in the track that was clicked in */
596
597                 last_frame = 0;
598                 first_frame = max_framepos;
599
600                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
601                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
602
603                                 if ((*x)->region()->last_frame() > last_frame) {
604                                         last_frame = (*x)->region()->last_frame();
605                                 }
606
607                                 if ((*x)->region()->first_frame() < first_frame) {
608                                         first_frame = (*x)->region()->first_frame();
609                                 }
610
611                                 same_track = true;
612                         }
613                 }
614
615                 if (same_track) {
616
617                         /* 2. figure out the boundaries for our search for new objects */
618
619                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
620                         case OverlapNone:
621                                 if (last_frame < clicked_regionview->region()->first_frame()) {
622                                         first_frame = last_frame;
623                                         last_frame = clicked_regionview->region()->last_frame();
624                                 } else {
625                                         last_frame = first_frame;
626                                         first_frame = clicked_regionview->region()->first_frame();
627                                 }
628                                 break;
629
630                         case OverlapExternal:
631                                 if (last_frame < clicked_regionview->region()->first_frame()) {
632                                         first_frame = last_frame;
633                                         last_frame = clicked_regionview->region()->last_frame();
634                                 } else {
635                                         last_frame = first_frame;
636                                         first_frame = clicked_regionview->region()->first_frame();
637                                 }
638                                 break;
639
640                         case OverlapInternal:
641                                 if (last_frame < clicked_regionview->region()->first_frame()) {
642                                         first_frame = last_frame;
643                                         last_frame = clicked_regionview->region()->last_frame();
644                                 } else {
645                                         last_frame = first_frame;
646                                         first_frame = clicked_regionview->region()->first_frame();
647                                 }
648                                 break;
649
650                         case OverlapStart:
651                         case OverlapEnd:
652                                 /* nothing to do except add clicked region to selection, since it
653                                    overlaps with the existing selection in this track.
654                                 */
655                                 break;
656                         }
657
658                 } else {
659
660                         /* click in a track that has no regions selected, so extend vertically
661                            to pick out all regions that are defined by the existing selection
662                            plus this one.
663                         */
664
665
666                         first_frame = clicked_regionview->region()->position();
667                         last_frame = clicked_regionview->region()->last_frame();
668
669                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
670                                 if ((*i)->region()->position() < first_frame) {
671                                         first_frame = (*i)->region()->position();
672                                 }
673                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
674                                         last_frame = (*i)->region()->last_frame();
675                                 }
676                         }
677                 }
678
679                 /* 2. find all the tracks we should select in */
680
681                 set<RouteTimeAxisView*> relevant_tracks;
682
683                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
684                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
685                         if (r) {
686                                 relevant_tracks.insert (r);
687                         }
688                 }
689                 
690                 set<RouteTimeAxisView*> already_in_selection;
691
692                 if (relevant_tracks.empty()) {
693
694                         /* no tracks selected .. thus .. if the
695                            regionview we're in isn't selected
696                            (i.e. we're about to extend to it), then
697                            find all tracks between the this one and
698                            any selected ones.
699                         */
700
701                         if (!selection->selected (clicked_regionview)) {
702
703                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
704
705                                 if (rtv) {
706
707                                         /* add this track to the ones we will search */
708
709                                         relevant_tracks.insert (rtv);
710
711                                         /* find the track closest to this one that
712                                            already a selected region.
713                                         */
714
715                                         RouteTimeAxisView* closest = 0;
716                                         int distance = INT_MAX;
717                                         int key = rtv->route()->order_key ("editor");
718
719                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
720
721                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
722
723                                                 if (artv && artv != rtv) {
724
725                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
726
727                                                         result = already_in_selection.insert (artv);
728
729                                                         if (result.second) {
730                                                                 /* newly added to already_in_selection */
731
732                                                                 int d = artv->route()->order_key ("editor");
733
734                                                                 d -= key;
735
736                                                                 if (abs (d) < distance) {
737                                                                         distance = abs (d);
738                                                                         closest = artv;
739                                                                 }
740                                                         }
741                                                 }
742                                         }
743
744                                         if (closest) {
745
746                                                 /* now add all tracks between that one and this one */
747
748                                                 int okey = closest->route()->order_key ("editor");
749
750                                                 if (okey > key) {
751                                                         swap (okey, key);
752                                                 }
753
754                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
755                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
756                                                         if (artv && artv != rtv) {
757
758                                                                 int k = artv->route()->order_key ("editor");
759
760                                                                 if (k >= okey && k <= key) {
761
762                                                                         /* in range but don't add it if
763                                                                            it already has tracks selected.
764                                                                            this avoids odd selection
765                                                                            behaviour that feels wrong.
766                                                                         */
767
768                                                                         if (find (already_in_selection.begin(),
769                                                                                   already_in_selection.end(),
770                                                                                   artv) == already_in_selection.end()) {
771
772                                                                                 relevant_tracks.insert (artv);
773                                                                         }
774                                                                 }
775                                                         }
776                                                 }
777                                         }
778                                 }
779                         }
780                 }
781
782                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
783                            one that was clicked.
784                 */
785
786                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
787                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
788                 }
789
790                 /* 4. convert to a vector of regions */
791
792                 vector<RegionView*> regions;
793
794                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
795                         RegionView* arv;
796
797                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
798                                 regions.push_back (arv);
799                         }
800                 }
801
802                 if (!regions.empty()) {
803                         selection->add (regions);
804                         commit = true;
805                 }
806         }
807
808   out:
809         return commit;
810 }
811
812
813 void
814 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
815 {
816         vector<RegionView*> all_equivalent_regions;
817
818         get_regions_corresponding_to (region, all_equivalent_regions);
819
820         if (all_equivalent_regions.empty()) {
821                 return;
822         }
823
824         begin_reversible_command (_("set selected regions"));
825
826         switch (op) {
827         case Selection::Toggle:
828                 /* XXX this is not correct */
829                 selection->toggle (all_equivalent_regions);
830                 break;
831         case Selection::Set:
832                 selection->set (all_equivalent_regions);
833                 break;
834         case Selection::Extend:
835                 selection->add (all_equivalent_regions);
836                 break;
837         case Selection::Add:
838                 selection->add (all_equivalent_regions);
839                 break;
840         }
841
842         commit_reversible_command () ;
843 }
844
845 bool
846 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
847 {
848         RegionView* rv;
849         boost::shared_ptr<Region> r (weak_r.lock());
850
851         if (!r) {
852                 return true;
853         }
854
855         if ((rv = sv->find_view (r)) == 0) {
856                 return true;
857         }
858
859         /* don't reset the selection if its something other than
860            a single other region.
861         */
862
863         if (selection->regions.size() > 1) {
864                 return true;
865         }
866
867         begin_reversible_command (_("set selected regions"));
868
869         selection->set (rv);
870
871         commit_reversible_command () ;
872
873         return true;
874 }
875
876 void
877 Editor::track_selection_changed ()
878 {
879         switch (selection->tracks.size()) {
880         case 0:
881                 break;
882         default:
883                 set_selected_mixer_strip (*(selection->tracks.front()));
884                 break;
885         }
886
887         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
888
889                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
890                 
891                 (*i)->set_selected (yn);
892                 
893                 TimeAxisView::Children c = (*i)->get_child_list ();
894                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
895                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
896                 }
897
898                 if (yn && 
899                     ((mouse_mode == MouseRange) || 
900                      ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
901                         (*i)->reshow_selection (selection->time);
902                 } else {
903                         (*i)->hide_selection ();
904                 }
905         }
906
907         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
908 }
909
910 void
911 Editor::time_selection_changed ()
912 {
913         if (Profile->get_sae()) {
914                 return;
915         }
916
917         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
918                 (*i)->hide_selection ();
919         }
920
921         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
922                 (*i)->show_selection (selection->time);
923         }
924
925         if (selection->time.empty()) {
926                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
927         } else {
928                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
929         }
930 }
931
932 /** Set all region actions to have a given sensitivity */
933 void
934 Editor::sensitize_all_region_actions (bool s)
935 {
936         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
937
938         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
939                 (*i)->set_sensitive (s);
940         }
941
942         _all_region_actions_sensitized = s;
943 }
944
945 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
946  *  This method should be called just before displaying a Region menu.  When a Region menu is not
947  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
948  *  on entered_regionviews work without having to check sensitivity every time the selection or
949  *  entered_regionview changes.
950  *
951  *  This method also sets up toggle action state as appropriate.
952  */
953 void
954 Editor::sensitize_the_right_region_actions ()
955 {
956         if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
957                 sensitize_all_region_actions (false);
958                 if (!selection->time.empty()) {
959                         _region_actions->get_action("split-region")->set_sensitive (true);
960                 }
961                 
962                 return;
963
964         } else if (mouse_mode != MouseObject) {
965                 sensitize_all_region_actions (false);
966                 return;
967         }
968
969         /* We get here if we are in Object mode */
970                         
971         RegionSelection rs = get_regions_from_selection_and_entered ();
972         sensitize_all_region_actions (!rs.empty ());
973
974         _ignore_region_action = true;
975         
976         /* Look through the regions that are selected and make notes about what we have got */
977         
978         bool have_audio = false;
979         bool have_midi = false;
980         bool have_locked = false;
981         bool have_unlocked = false;
982         bool have_position_lock_style_audio = false;
983         bool have_position_lock_style_music = false;
984         bool have_muted = false;
985         bool have_unmuted = false;
986         bool have_opaque = false;
987         bool have_non_opaque = false;
988         bool have_not_at_natural_position = false;
989         bool have_envelope_visible = false;
990         bool have_envelope_invisible = false;
991         bool have_envelope_active = false;
992         bool have_envelope_inactive = false;
993         bool have_non_unity_scale_amplitude = false;
994
995         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
996
997                 boost::shared_ptr<Region> r = (*i)->region ();
998                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
999                 
1000                 if (ar) {
1001                         have_audio = true;
1002                 }
1003                 
1004                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1005                         have_midi = true;
1006                 }
1007
1008                 if (r->locked()) {
1009                         have_locked = true;
1010                 } else {
1011                         have_unlocked = true;
1012                 }
1013
1014                 if (r->position_lock_style() == MusicTime) {
1015                         have_position_lock_style_music = true;
1016                 } else {
1017                         have_position_lock_style_audio = true;
1018                 }
1019
1020                 if (r->muted()) {
1021                         have_muted = true;
1022                 } else {
1023                         have_unmuted = true;
1024                 }
1025
1026                 if (r->opaque()) {
1027                         have_opaque = true;
1028                 } else {
1029                         have_non_opaque = true;
1030                 }
1031
1032                 if (!r->at_natural_position()) {
1033                         have_not_at_natural_position = true;
1034                 }
1035
1036                 if (ar) {
1037                         /* its a bit unfortunate that "envelope visible" is a view-only
1038                            property. we have to find the regionview to able to check
1039                            its current setting.
1040                         */
1041
1042                         have_envelope_invisible = true;
1043
1044                         if (*i) {
1045                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1046                                 if (arv && arv->envelope_visible()) {
1047                                         have_envelope_visible = true;
1048                                 }
1049                         }
1050
1051                         if (ar->envelope_active()) {
1052                                 have_envelope_active = true;
1053                         } else {
1054                                 have_envelope_inactive = true;
1055                         }
1056
1057                         if (ar->scale_amplitude() != 1) {
1058                                 have_non_unity_scale_amplitude = true;
1059                         }
1060                 }
1061         }
1062
1063         if (rs.size() > 1) {
1064                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1065                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1066                 _region_actions->get_action("rename-region")->set_sensitive (false);
1067         } else if (rs.size() == 1) {
1068                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1069                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1070         } 
1071
1072         if (!have_midi) {
1073                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1074                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1075                 _region_actions->get_action("fork-region")->set_sensitive (false);
1076         }
1077
1078         if (_edit_point == EditAtMouse) {
1079                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1080                 _region_actions->get_action("trim-front")->set_sensitive (false);
1081                 _region_actions->get_action("trim-back")->set_sensitive (false);
1082                 _region_actions->get_action("split-region")->set_sensitive (false);
1083                 _region_actions->get_action("place-transient")->set_sensitive (false);
1084         }
1085
1086         if (have_audio) {
1087                 
1088                 if (have_envelope_visible && !have_envelope_invisible) {
1089                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1090                 } else if (have_envelope_visible && have_envelope_invisible) {
1091 //                      _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1092                 }
1093                 
1094                 if (have_envelope_active && !have_envelope_inactive) {
1095                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1096                 } else if (have_envelope_active && have_envelope_inactive) {
1097 //                      _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1098                 }
1099         
1100         } else {
1101                 
1102                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1103                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1104                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1105                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1106                 
1107         }
1108
1109         if (!have_non_unity_scale_amplitude || !have_audio) {
1110                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1111         }
1112                 
1113         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1114         if (have_locked && have_unlocked) {
1115 //              _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1116         }
1117
1118         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1119                 
1120         if (have_position_lock_style_music && have_position_lock_style_audio) {
1121 //              _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1122         }
1123
1124         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1125         if (have_muted && have_unmuted) {
1126 //              _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1127         }
1128         
1129         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1130         if (have_opaque && have_non_opaque) {
1131 //                      _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1132         }
1133
1134         if (!have_not_at_natural_position) {
1135                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1136         }
1137
1138         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1139         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1140                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1141         } else {
1142                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1143         }
1144
1145         _ignore_region_action = false;
1146         
1147         _all_region_actions_sensitized = false;
1148 }
1149
1150
1151 void
1152 Editor::region_selection_changed ()
1153 {
1154         _regions->block_change_connection (true);
1155         editor_regions_selection_changed_connection.block(true);
1156
1157         if (_region_selection_change_updates_region_list) {
1158                 _regions->unselect_all ();
1159         }
1160
1161         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1162                 (*i)->set_selected_regionviews (selection->regions);
1163         }
1164
1165         if (_region_selection_change_updates_region_list) {
1166                 _regions->set_selected (selection->regions);
1167         }
1168
1169         _regions->block_change_connection (false);
1170         editor_regions_selection_changed_connection.block(false);
1171
1172         if (!_all_region_actions_sensitized) {
1173                 /* This selection change might have changed what region actions
1174                    are allowed, so sensitize them all in case a key is pressed.
1175                 */
1176                 sensitize_all_region_actions (true);
1177         }
1178 }
1179
1180 void
1181 Editor::point_selection_changed ()
1182 {
1183         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1184                 (*i)->set_selected_points (selection->points);
1185         }
1186 }
1187
1188 void
1189 Editor::select_all_in_track (Selection::Operation op)
1190 {
1191         list<Selectable *> touched;
1192
1193         if (!clicked_routeview) {
1194                 return;
1195         }
1196
1197         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1198
1199         switch (op) {
1200         case Selection::Toggle:
1201                 selection->add (touched);
1202                 break;
1203         case Selection::Set:
1204                 selection->set (touched);
1205                 break;
1206         case Selection::Extend:
1207                 /* meaningless, because we're selecting everything */
1208                 break;
1209         case Selection::Add:
1210                 selection->add (touched);
1211                 break;
1212         }
1213 }
1214
1215 void
1216 Editor::select_all (Selection::Operation op)
1217 {
1218         list<Selectable *> touched;
1219
1220         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1221                 if ((*iter)->hidden()) {
1222                         continue;
1223                 }
1224                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1225         }
1226         begin_reversible_command (_("select all"));
1227         switch (op) {
1228         case Selection::Add:
1229                 selection->add (touched);
1230                 break;
1231         case Selection::Toggle:
1232                 selection->add (touched);
1233                 break;
1234         case Selection::Set:
1235                 selection->set (touched);
1236                 break;
1237         case Selection::Extend:
1238                 /* meaningless, because we're selecting everything */
1239                 break;
1240         }
1241         commit_reversible_command ();
1242 }
1243 void
1244 Editor::invert_selection_in_track ()
1245 {
1246         list<Selectable *> touched;
1247
1248         if (!clicked_routeview) {
1249                 return;
1250         }
1251
1252         clicked_routeview->get_inverted_selectables (*selection, touched);
1253         selection->set (touched);
1254 }
1255
1256 void
1257 Editor::invert_selection ()
1258 {
1259         list<Selectable *> touched;
1260
1261         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1262                 if ((*iter)->hidden()) {
1263                         continue;
1264                 }
1265                 (*iter)->get_inverted_selectables (*selection, touched);
1266         }
1267
1268         selection->set (touched);
1269 }
1270
1271 /** @param start Start time in session frames.
1272  *  @param end End time in session frames.
1273  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1274  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1275  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1276  *  within the region are already selected.
1277  */
1278 void
1279 Editor::select_all_within (
1280         framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected
1281         )
1282 {
1283         list<Selectable*> found;
1284
1285         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1286                 
1287                 if ((*iter)->hidden()) {
1288                         continue;
1289                 }
1290
1291                 (*iter)->get_selectables (start, end, top, bot, found);
1292         }
1293
1294         if (found.empty()) {
1295                 return;
1296         }
1297
1298         if (preserve_if_selected && op != Selection::Toggle) {
1299                 list<Selectable*>::iterator i = found.begin();
1300                 while (i != found.end() && (*i)->get_selected()) {
1301                         ++i;
1302                 }
1303
1304                 if (i == found.end()) {
1305                         return;
1306                 }
1307         }
1308
1309         begin_reversible_command (_("select all within"));
1310         switch (op) {
1311         case Selection::Add:
1312                 selection->add (found);
1313                 break;
1314         case Selection::Toggle:
1315                 selection->toggle (found);
1316                 break;
1317         case Selection::Set:
1318                 selection->set (found);
1319                 break;
1320         case Selection::Extend:
1321                 /* not defined yet */
1322                 break;
1323         }
1324
1325         commit_reversible_command ();
1326 }
1327
1328 void
1329 Editor::set_selection_from_region ()
1330 {
1331         if (selection->regions.empty()) {
1332                 return;
1333         }
1334
1335         selection->set (selection->regions.start(), selection->regions.end_frame());
1336         if (!Profile->get_sae()) {
1337                 set_mouse_mode (Editing::MouseRange, false);
1338         }
1339 }
1340
1341 void
1342 Editor::set_selection_from_punch()
1343 {
1344         Location* location;
1345
1346         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1347                 return;
1348         }
1349
1350         set_selection_from_range (*location);
1351 }
1352
1353 void
1354 Editor::set_selection_from_loop()
1355 {
1356         Location* location;
1357
1358         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1359                 return;
1360         }
1361         set_selection_from_range (*location);
1362 }
1363
1364 void
1365 Editor::set_selection_from_range (Location& loc)
1366 {
1367         begin_reversible_command (_("set selection from range"));
1368         selection->set (loc.start(), loc.end());
1369         commit_reversible_command ();
1370
1371         if (!Profile->get_sae()) {
1372                 set_mouse_mode (Editing::MouseRange, false);
1373         }
1374 }
1375
1376 void
1377 Editor::select_all_selectables_using_time_selection ()
1378 {
1379         list<Selectable *> touched;
1380
1381         if (selection->time.empty()) {
1382                 return;
1383         }
1384
1385         framepos_t start = selection->time[clicked_selection].start;
1386         framepos_t end = selection->time[clicked_selection].end;
1387
1388         if (end - start < 1)  {
1389                 return;
1390         }
1391
1392         TrackViewList* ts;
1393
1394         if (selection->tracks.empty()) {
1395                 ts = &track_views;
1396         } else {
1397                 ts = &selection->tracks;
1398         }
1399
1400         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1401                 if ((*iter)->hidden()) {
1402                         continue;
1403                 }
1404                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1405         }
1406
1407         begin_reversible_command (_("select all from range"));
1408         selection->set (touched);
1409         commit_reversible_command ();
1410 }
1411
1412
1413 void
1414 Editor::select_all_selectables_using_punch()
1415 {
1416         Location* location = _session->locations()->auto_punch_location();
1417         list<Selectable *> touched;
1418
1419         if (location == 0 || (location->end() - location->start() <= 1))  {
1420                 return;
1421         }
1422
1423
1424         TrackViewList* ts;
1425
1426         if (selection->tracks.empty()) {
1427                 ts = &track_views;
1428         } else {
1429                 ts = &selection->tracks;
1430         }
1431
1432         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1433                 if ((*iter)->hidden()) {
1434                         continue;
1435                 }
1436                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1437         }
1438         begin_reversible_command (_("select all from punch"));
1439         selection->set (touched);
1440         commit_reversible_command ();
1441
1442 }
1443
1444 void
1445 Editor::select_all_selectables_using_loop()
1446 {
1447         Location* location = _session->locations()->auto_loop_location();
1448         list<Selectable *> touched;
1449
1450         if (location == 0 || (location->end() - location->start() <= 1))  {
1451                 return;
1452         }
1453
1454
1455         TrackViewList* ts;
1456
1457         if (selection->tracks.empty()) {
1458                 ts = &track_views;
1459         } else {
1460                 ts = &selection->tracks;
1461         }
1462
1463         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1464                 if ((*iter)->hidden()) {
1465                         continue;
1466                 }
1467                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1468         }
1469         begin_reversible_command (_("select all from loop"));
1470         selection->set (touched);
1471         commit_reversible_command ();
1472
1473 }
1474
1475 void
1476 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1477 {
1478         framepos_t start;
1479         framepos_t end;
1480         list<Selectable *> touched;
1481
1482         if (after) {
1483                 begin_reversible_command (_("select all after cursor"));
1484                 start = cursor->current_frame;
1485                 end = _session->current_end_frame();
1486         } else {
1487                 if (cursor->current_frame > 0) {
1488                         begin_reversible_command (_("select all before cursor"));
1489                         start = 0;
1490                         end = cursor->current_frame - 1;
1491                 } else {
1492                         return;
1493                 }
1494         }
1495
1496
1497         TrackViewList* ts;
1498
1499         if (selection->tracks.empty()) {
1500                 ts = &track_views;
1501         } else {
1502                 ts = &selection->tracks;
1503         }
1504
1505         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1506                 if ((*iter)->hidden()) {
1507                         continue;
1508                 }
1509                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1510         }
1511         selection->set (touched);
1512         commit_reversible_command ();
1513 }
1514
1515 void
1516 Editor::select_all_selectables_using_edit (bool after)
1517 {
1518         framepos_t start;
1519         framepos_t end;
1520         list<Selectable *> touched;
1521
1522         if (after) {
1523                 begin_reversible_command (_("select all after edit"));
1524                 start = get_preferred_edit_position();
1525                 end = _session->current_end_frame();
1526         } else {
1527                 if ((end = get_preferred_edit_position()) > 1) {
1528                         begin_reversible_command (_("select all before edit"));
1529                         start = 0;
1530                         end -= 1;
1531                 } else {
1532                         return;
1533                 }
1534         }
1535
1536
1537         TrackViewList* ts;
1538
1539         if (selection->tracks.empty()) {
1540                 ts = &track_views;
1541         } else {
1542                 ts = &selection->tracks;
1543         }
1544
1545         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1546                 if ((*iter)->hidden()) {
1547                         continue;
1548                 }
1549                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1550         }
1551         selection->set (touched);
1552         commit_reversible_command ();
1553 }
1554
1555 void
1556 Editor::select_all_selectables_between (bool /*within*/)
1557 {
1558         framepos_t start;
1559         framepos_t end;
1560         list<Selectable *> touched;
1561
1562         if (!get_edit_op_range (start, end)) {
1563                 return;
1564         }
1565
1566         TrackViewList* ts;
1567
1568         if (selection->tracks.empty()) {
1569                 ts = &track_views;
1570         } else {
1571                 ts = &selection->tracks;
1572         }
1573
1574         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1575                 if ((*iter)->hidden()) {
1576                         continue;
1577                 }
1578                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1579         }
1580
1581         selection->set (touched);
1582 }
1583
1584 void
1585 Editor::select_range_between ()
1586 {
1587         framepos_t start;
1588         framepos_t end;
1589
1590         if (mouse_mode == MouseRange && !selection->time.empty()) {
1591                 selection->clear_time ();
1592         }
1593
1594         if (!get_edit_op_range (start, end)) {
1595                 return;
1596         }
1597
1598         set_mouse_mode (MouseRange);
1599         selection->set (start, end);
1600 }
1601
1602 bool
1603 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1604 {
1605         framepos_t m;
1606         bool ignored;
1607
1608         /* in range mode, use any existing selection */
1609
1610         if (mouse_mode == MouseRange && !selection->time.empty()) {
1611                 /* we know that these are ordered */
1612                 start = selection->time.start();
1613                 end = selection->time.end_frame();
1614                 return true;
1615         }
1616
1617         if (!mouse_frame (m, ignored)) {
1618                 /* mouse is not in a canvas, try playhead+selected marker.
1619                    this is probably most true when using menus.
1620                  */
1621
1622                 if (selection->markers.empty()) {
1623                         return false;
1624                 }
1625
1626                 start = selection->markers.front()->position();
1627                 end = _session->audible_frame();
1628
1629         } else {
1630
1631                 switch (_edit_point) {
1632                 case EditAtPlayhead:
1633                         if (selection->markers.empty()) {
1634                                 /* use mouse + playhead */
1635                                 start = m;
1636                                 end = _session->audible_frame();
1637                         } else {
1638                                 /* use playhead + selected marker */
1639                                 start = _session->audible_frame();
1640                                 end = selection->markers.front()->position();
1641                         }
1642                         break;
1643
1644                 case EditAtMouse:
1645                         /* use mouse + selected marker */
1646                         if (selection->markers.empty()) {
1647                                 start = m;
1648                                 end = _session->audible_frame();
1649                         } else {
1650                                 start = selection->markers.front()->position();
1651                                 end = m;
1652                         }
1653                         break;
1654
1655                 case EditAtSelectedMarker:
1656                         /* use mouse + selected marker */
1657                         if (selection->markers.empty()) {
1658
1659                                 MessageDialog win (_("No edit range defined"),
1660                                                    false,
1661                                                    MESSAGE_INFO,
1662                                                    BUTTONS_OK);
1663
1664                                 win.set_secondary_text (
1665                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1666
1667
1668                                 win.set_default_response (RESPONSE_CLOSE);
1669                                 win.set_position (Gtk::WIN_POS_MOUSE);
1670                                 win.show_all();
1671
1672                                 win.run ();
1673
1674                                 return false; // NO RANGE
1675                         }
1676                         start = selection->markers.front()->position();
1677                         end = m;
1678                         break;
1679                 }
1680         }
1681
1682         if (start == end) {
1683                 return false;
1684         }
1685
1686         if (start > end) {
1687                 swap (start, end);
1688         }
1689
1690         /* turn range into one delimited by start...end,
1691            not start...end-1
1692         */
1693
1694         end++;
1695
1696         return true;
1697 }
1698
1699 void
1700 Editor::deselect_all ()
1701 {
1702         selection->clear ();
1703 }
1704
1705 long
1706 Editor::select_range_around_region (RegionView* rv)
1707 {
1708         assert (rv);
1709         
1710         selection->set (&rv->get_time_axis_view());
1711         
1712         selection->time.clear ();
1713         boost::shared_ptr<Region> r = rv->region ();
1714         return selection->set (r->position(), r->position() + r->length());
1715 }