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