142204f9b4864992404e73146cd8a6db68cd4d85
[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 /** Call a slot for a given `basis' track and also for any track that is in the same
405  *  active route group with a particular set of properties.
406  *
407  *  @param sl Slot to call.
408  *  @param basis Basis track.
409  *  @param prop Properties that active edit groups must share to be included in the map.
410  */
411
412 void
413 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
414 {
415         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
416         set<boost::shared_ptr<Playlist> > playlists;
417
418         if (route_basis == 0) {
419                 return;
420         }
421
422         set<RouteTimeAxisView*> tracks;
423         tracks.insert (route_basis);
424
425         RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
426
427         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
428
429                 /* the basis is a member of an active route group, with the appropriate
430                    properties; find other members */
431
432                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
433                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
434
435                         if (v && v->route()->route_group() == group) {
436                                 
437                                 boost::shared_ptr<Track> t = v->track();
438                                 if (t) {
439                                         if (playlists.insert (t->playlist()).second) {
440                                                 /* haven't seen this playlist yet */
441                                                 tracks.insert (v);
442                                         }
443                                 } else {
444                                         /* not actually a "Track", but a timeaxis view that
445                                            we should mapover anyway.
446                                         */
447                                         tracks.insert (v);
448                                 }
449                         }
450                 }
451         }
452
453         /* call the slots */
454         uint32_t const sz = tracks.size ();
455
456         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
457                 sl (**i, sz);
458         }
459 }
460
461 void
462 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
463 {
464         boost::shared_ptr<Playlist> pl;
465         vector<boost::shared_ptr<Region> > results;
466         RegionView* marv;
467         boost::shared_ptr<Track> tr;
468
469         if ((tr = tv.track()) == 0) {
470                 /* bus */
471                 return;
472         }
473
474         if (&tv == &basis->get_time_axis_view()) {
475                 /* looking in same track as the original */
476                 return;
477         }
478
479         if ((pl = tr->playlist()) != 0) {
480                 pl->get_equivalent_regions (basis->region(), results);
481         }
482
483         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
484                 if ((marv = tv.view()->find_view (*ir)) != 0) {
485                         all_equivs->push_back (marv);
486                 }
487         }
488 }
489
490 void
491 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
492 {
493         mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
494
495         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
496
497         equivalent_regions.push_back (basis);
498 }
499
500 RegionSelection
501 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
502 {
503         RegionSelection equivalent;
504
505         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
506
507                 vector<RegionView*> eq;
508
509                 mapover_tracks_with_unique_playlists (
510                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
511                         &(*i)->get_time_axis_view(), prop);
512
513                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
514                         equivalent.add (*j);
515                 }
516
517                 equivalent.add (*i);
518         }
519
520         return equivalent;
521 }
522
523
524 int
525 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
526 {
527         int region_count = 0;
528
529         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
530
531                 RouteTimeAxisView* tatv;
532
533                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
534
535                         boost::shared_ptr<Playlist> pl;
536                         vector<boost::shared_ptr<Region> > results;
537                         RegionView* marv;
538                         boost::shared_ptr<Track> tr;
539
540                         if ((tr = tatv->track()) == 0) {
541                                 /* bus */
542                                 continue;
543                         }
544
545                         if ((pl = (tr->playlist())) != 0) {
546                                 pl->get_region_list_equivalent_regions (region, results);
547                         }
548
549                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
550                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
551                                         region_count++;
552                                 }
553                         }
554
555                 }
556         }
557
558         return region_count;
559 }
560
561
562 bool
563 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
564 {
565         vector<RegionView*> all_equivalent_regions;
566         bool commit = false;
567
568         if (!clicked_regionview || !clicked_routeview) {
569                 return false;
570         }
571
572         if (press) {
573                 button_release_can_deselect = false;
574         }
575
576         if (op == Selection::Toggle || op == Selection::Set) {
577
578
579                 switch (op) {
580                 case Selection::Toggle:
581                         if (selection->selected (clicked_regionview)) {
582                                 if (press) {
583
584                                         /* whatever was clicked was selected already; do nothing here but allow
585                                            the button release to deselect it
586                                         */
587
588                                         button_release_can_deselect = true;
589
590                                 } else {
591                                         if (button_release_can_deselect) {
592
593                                                 /* just remove this one region, but only on a permitted button release */
594
595                                                 selection->remove (clicked_regionview);
596                                                 commit = true;
597
598                                                 /* no more deselect action on button release till a new press
599                                                    finds an already selected object.
600                                                 */
601
602                                                 button_release_can_deselect = false;
603                                         }
604                                 }
605
606                         } else {
607
608                                 if (press) {
609
610                                         if (selection->selected (clicked_routeview)) {
611                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
612                                         } else {
613                                                 all_equivalent_regions.push_back (clicked_regionview);
614                                         }
615
616                                         /* add all the equivalent regions, but only on button press */
617
618                                         if (!all_equivalent_regions.empty()) {
619                                                 commit = true;
620                                         }
621
622                                         selection->add (all_equivalent_regions);
623                                 }
624                         }
625                         break;
626
627                 case Selection::Set:
628                         if (!selection->selected (clicked_regionview)) {
629                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
630                                 selection->set (all_equivalent_regions);
631                                 commit = true;
632                         } else {
633                                 /* no commit necessary: clicked on an already selected region */
634                                 goto out;
635                         }
636                         break;
637
638                 default:
639                         /* silly compiler */
640                         break;
641                 }
642
643         } else if (op == Selection::Extend) {
644
645                 list<Selectable*> results;
646                 framepos_t last_frame;
647                 framepos_t first_frame;
648                 bool same_track = false;
649
650                 /* 1. find the last selected regionview in the track that was clicked in */
651
652                 last_frame = 0;
653                 first_frame = max_framepos;
654
655                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
656                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
657
658                                 if ((*x)->region()->last_frame() > last_frame) {
659                                         last_frame = (*x)->region()->last_frame();
660                                 }
661
662                                 if ((*x)->region()->first_frame() < first_frame) {
663                                         first_frame = (*x)->region()->first_frame();
664                                 }
665
666                                 same_track = true;
667                         }
668                 }
669
670                 if (same_track) {
671
672                         /* 2. figure out the boundaries for our search for new objects */
673
674                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
675                         case OverlapNone:
676                                 if (last_frame < clicked_regionview->region()->first_frame()) {
677                                         first_frame = last_frame;
678                                         last_frame = clicked_regionview->region()->last_frame();
679                                 } else {
680                                         last_frame = first_frame;
681                                         first_frame = clicked_regionview->region()->first_frame();
682                                 }
683                                 break;
684
685                         case OverlapExternal:
686                                 if (last_frame < clicked_regionview->region()->first_frame()) {
687                                         first_frame = last_frame;
688                                         last_frame = clicked_regionview->region()->last_frame();
689                                 } else {
690                                         last_frame = first_frame;
691                                         first_frame = clicked_regionview->region()->first_frame();
692                                 }
693                                 break;
694
695                         case OverlapInternal:
696                                 if (last_frame < clicked_regionview->region()->first_frame()) {
697                                         first_frame = last_frame;
698                                         last_frame = clicked_regionview->region()->last_frame();
699                                 } else {
700                                         last_frame = first_frame;
701                                         first_frame = clicked_regionview->region()->first_frame();
702                                 }
703                                 break;
704
705                         case OverlapStart:
706                         case OverlapEnd:
707                                 /* nothing to do except add clicked region to selection, since it
708                                    overlaps with the existing selection in this track.
709                                 */
710                                 break;
711                         }
712
713                 } else {
714
715                         /* click in a track that has no regions selected, so extend vertically
716                            to pick out all regions that are defined by the existing selection
717                            plus this one.
718                         */
719
720
721                         first_frame = clicked_regionview->region()->position();
722                         last_frame = clicked_regionview->region()->last_frame();
723
724                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
725                                 if ((*i)->region()->position() < first_frame) {
726                                         first_frame = (*i)->region()->position();
727                                 }
728                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
729                                         last_frame = (*i)->region()->last_frame();
730                                 }
731                         }
732                 }
733
734                 /* 2. find all the tracks we should select in */
735
736                 set<RouteTimeAxisView*> relevant_tracks;
737
738                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
739                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
740                         if (r) {
741                                 relevant_tracks.insert (r);
742                         }
743                 }
744
745                 set<RouteTimeAxisView*> already_in_selection;
746
747                 if (relevant_tracks.empty()) {
748
749                         /* no tracks selected .. thus .. if the
750                            regionview we're in isn't selected
751                            (i.e. we're about to extend to it), then
752                            find all tracks between the this one and
753                            any selected ones.
754                         */
755
756                         if (!selection->selected (clicked_regionview)) {
757
758                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
759
760                                 if (rtv) {
761
762                                         /* add this track to the ones we will search */
763
764                                         relevant_tracks.insert (rtv);
765
766                                         /* find the track closest to this one that
767                                            already a selected region.
768                                         */
769
770                                         RouteTimeAxisView* closest = 0;
771                                         int distance = INT_MAX;
772                                         int key = rtv->route()->order_key ("editor");
773
774                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
775
776                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
777
778                                                 if (artv && artv != rtv) {
779
780                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
781
782                                                         result = already_in_selection.insert (artv);
783
784                                                         if (result.second) {
785                                                                 /* newly added to already_in_selection */
786
787                                                                 int d = artv->route()->order_key ("editor");
788
789                                                                 d -= key;
790
791                                                                 if (abs (d) < distance) {
792                                                                         distance = abs (d);
793                                                                         closest = artv;
794                                                                 }
795                                                         }
796                                                 }
797                                         }
798
799                                         if (closest) {
800
801                                                 /* now add all tracks between that one and this one */
802
803                                                 int okey = closest->route()->order_key ("editor");
804
805                                                 if (okey > key) {
806                                                         swap (okey, key);
807                                                 }
808
809                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
810                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
811                                                         if (artv && artv != rtv) {
812
813                                                                 int k = artv->route()->order_key ("editor");
814
815                                                                 if (k >= okey && k <= key) {
816
817                                                                         /* in range but don't add it if
818                                                                            it already has tracks selected.
819                                                                            this avoids odd selection
820                                                                            behaviour that feels wrong.
821                                                                         */
822
823                                                                         if (find (already_in_selection.begin(),
824                                                                                   already_in_selection.end(),
825                                                                                   artv) == already_in_selection.end()) {
826
827                                                                                 relevant_tracks.insert (artv);
828                                                                         }
829                                                                 }
830                                                         }
831                                                 }
832                                         }
833                                 }
834                         }
835                 }
836
837                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
838                    one that was clicked.
839                 */
840
841                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
842                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
843                 }
844
845                 /* 4. convert to a vector of regions */
846
847                 vector<RegionView*> regions;
848
849                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
850                         RegionView* arv;
851
852                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
853                                 regions.push_back (arv);
854                         }
855                 }
856
857                 if (!regions.empty()) {
858                         selection->add (regions);
859                         commit = true;
860                 }
861         }
862
863 out:
864         return commit;
865 }
866
867
868 void
869 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
870 {
871         vector<RegionView*> all_equivalent_regions;
872
873         get_regions_corresponding_to (region, all_equivalent_regions);
874
875         if (all_equivalent_regions.empty()) {
876                 return;
877         }
878
879         begin_reversible_command (_("set selected regions"));
880
881         switch (op) {
882         case Selection::Toggle:
883                 /* XXX this is not correct */
884                 selection->toggle (all_equivalent_regions);
885                 break;
886         case Selection::Set:
887                 selection->set (all_equivalent_regions);
888                 break;
889         case Selection::Extend:
890                 selection->add (all_equivalent_regions);
891                 break;
892         case Selection::Add:
893                 selection->add (all_equivalent_regions);
894                 break;
895         }
896
897         commit_reversible_command () ;
898 }
899
900 bool
901 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
902 {
903         RegionView* rv;
904         boost::shared_ptr<Region> r (weak_r.lock());
905
906         if (!r) {
907                 return true;
908         }
909
910         if ((rv = sv->find_view (r)) == 0) {
911                 return true;
912         }
913
914         /* don't reset the selection if its something other than
915            a single other region.
916         */
917
918         if (selection->regions.size() > 1) {
919                 return true;
920         }
921
922         begin_reversible_command (_("set selected regions"));
923
924         selection->set (rv);
925
926         commit_reversible_command () ;
927
928         return true;
929 }
930
931 void
932 Editor::track_selection_changed ()
933 {
934         switch (selection->tracks.size()) {
935         case 0:
936                 break;
937         default:
938                 set_selected_mixer_strip (*(selection->tracks.front()));
939                 break;
940         }
941
942         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
943
944                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
945
946                 (*i)->set_selected (yn);
947
948                 TimeAxisView::Children c = (*i)->get_child_list ();
949                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
950                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
951                 }
952
953                 if (yn &&
954                     ((mouse_mode == MouseRange) ||
955                      ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
956                         (*i)->reshow_selection (selection->time);
957                 } else {
958                         (*i)->hide_selection ();
959                 }
960         }
961
962         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
963 }
964
965 void
966 Editor::time_selection_changed ()
967 {
968         if (Profile->get_sae()) {
969                 return;
970         }
971
972         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
973                 (*i)->hide_selection ();
974         }
975
976         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
977                 (*i)->show_selection (selection->time);
978         }
979
980         if (selection->time.empty()) {
981                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
982         } else {
983                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
984         }
985 }
986
987 /** Set all region actions to have a given sensitivity */
988 void
989 Editor::sensitize_all_region_actions (bool s)
990 {
991         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
992
993         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
994                 (*i)->set_sensitive (s);
995         }
996
997         _all_region_actions_sensitized = s;
998 }
999
1000 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1001  *  This method should be called just before displaying a Region menu.  When a Region menu is not
1002  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
1003  *  on entered_regionviews work without having to check sensitivity every time the selection or
1004  *  entered_regionview changes.
1005  *
1006  *  This method also sets up toggle action state as appropriate.
1007  */
1008 void
1009 Editor::sensitize_the_right_region_actions ()
1010 {
1011         if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1012                 sensitize_all_region_actions (false);
1013                 if (!selection->time.empty()) {
1014                         _region_actions->get_action("split-region")->set_sensitive (true);
1015                 }
1016                 return;
1017
1018         } else if (mouse_mode != MouseObject) {
1019                 sensitize_all_region_actions (false);
1020                 return;
1021         }
1022
1023         /* We get here if we are in Object mode */
1024
1025         RegionSelection rs = get_regions_from_selection_and_entered ();
1026         sensitize_all_region_actions (!rs.empty ());
1027
1028         _ignore_region_action = true;
1029
1030         /* Look through the regions that are selected and make notes about what we have got */
1031
1032         bool have_audio = false;
1033         bool have_multichannel_audio = false;
1034         bool have_midi = false;
1035         bool have_locked = false;
1036         bool have_unlocked = false;
1037         bool have_position_lock_style_audio = false;
1038         bool have_position_lock_style_music = false;
1039         bool have_muted = false;
1040         bool have_unmuted = false;
1041         bool have_opaque = false;
1042         bool have_non_opaque = false;
1043         bool have_not_at_natural_position = false;
1044         bool have_envelope_visible = false;
1045         bool have_envelope_invisible = false;
1046         bool have_envelope_active = false;
1047         bool have_envelope_inactive = false;
1048         bool have_non_unity_scale_amplitude = false;
1049         bool have_compound_regions = false;
1050
1051         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1052
1053                 boost::shared_ptr<Region> r = (*i)->region ();
1054                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1055
1056                 if (ar) {
1057                         have_audio = true;
1058                         if (ar->n_channels() > 1) {
1059                                 have_multichannel_audio = true;
1060                         }
1061                 }
1062
1063                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1064                         have_midi = true;
1065                 }
1066
1067                 if (r->is_compound()) {
1068                         have_compound_regions = true;
1069                 }
1070
1071                 if (r->locked()) {
1072                         have_locked = true;
1073                 } else {
1074                         have_unlocked = true;
1075                 }
1076
1077                 if (r->position_lock_style() == MusicTime) {
1078                         have_position_lock_style_music = true;
1079                 } else {
1080                         have_position_lock_style_audio = true;
1081                 }
1082
1083                 if (r->muted()) {
1084                         have_muted = true;
1085                 } else {
1086                         have_unmuted = true;
1087                 }
1088
1089                 if (r->opaque()) {
1090                         have_opaque = true;
1091                 } else {
1092                         have_non_opaque = true;
1093                 }
1094
1095                 if (!r->at_natural_position()) {
1096                         have_not_at_natural_position = true;
1097                 }
1098
1099                 if (ar) {
1100                         /* its a bit unfortunate that "envelope visible" is a view-only
1101                            property. we have to find the regionview to able to check
1102                            its current setting.
1103                         */
1104
1105                         have_envelope_invisible = false;
1106
1107                         if (*i) {
1108                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1109                                 if (arv) {
1110                                         if (arv->envelope_visible()) {
1111                                                 have_envelope_visible = true;
1112                                         } else {
1113                                                 have_envelope_invisible = true;
1114                                         }
1115                                 }
1116                         }
1117
1118                         if (ar->envelope_active()) {
1119                                 have_envelope_active = true;
1120                         } else {
1121                                 have_envelope_inactive = true;
1122                         }
1123
1124                         if (ar->scale_amplitude() != 1) {
1125                                 have_non_unity_scale_amplitude = true;
1126                         }
1127                 }
1128         }
1129
1130         if (rs.size() > 1) {
1131                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1132                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1133                 _region_actions->get_action("rename-region")->set_sensitive (false);
1134                 if (have_audio) {
1135                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1136                 } else {
1137                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1138                 }
1139         } else if (rs.size() == 1) {
1140                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1141                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1142                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1143         }
1144
1145         if (!have_multichannel_audio) {
1146                 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1147         }
1148
1149         if (!have_midi) {
1150                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1151                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1152                 _region_actions->get_action("fork-region")->set_sensitive (false);
1153                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1154         }
1155
1156         if (_edit_point == EditAtMouse) {
1157                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1158                 _region_actions->get_action("trim-front")->set_sensitive (false);
1159                 _region_actions->get_action("trim-back")->set_sensitive (false);
1160                 _region_actions->get_action("split-region")->set_sensitive (false);
1161                 _region_actions->get_action("place-transient")->set_sensitive (false);
1162         }
1163
1164         if (have_compound_regions) {
1165                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1166         } else {
1167                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1168         }
1169
1170         if (have_audio) {
1171
1172                 if (have_envelope_visible && !have_envelope_invisible) {
1173                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1174                 } else if (have_envelope_visible && have_envelope_invisible) {
1175                         // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1176                 }
1177
1178                 if (have_envelope_active && !have_envelope_inactive) {
1179                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1180                 } else if (have_envelope_active && have_envelope_inactive) {
1181                         // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1182                 }
1183
1184         } else {
1185
1186                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1187                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1188                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1189                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1190                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1191
1192         }
1193
1194         if (!have_non_unity_scale_amplitude || !have_audio) {
1195                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1196         }
1197
1198         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1199         if (have_locked && have_unlocked) {
1200                 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1201         }
1202
1203         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);
1204
1205         if (have_position_lock_style_music && have_position_lock_style_audio) {
1206                 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1207         }
1208
1209         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1210         if (have_muted && have_unmuted) {
1211                 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1212         }
1213
1214         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1215         if (have_opaque && have_non_opaque) {
1216                 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1217         }
1218
1219         if (!have_not_at_natural_position) {
1220                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1221         }
1222
1223         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1224         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1225                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1226         } else {
1227                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1228         }
1229
1230         _ignore_region_action = false;
1231
1232         _all_region_actions_sensitized = false;
1233 }
1234
1235
1236 void
1237 Editor::region_selection_changed ()
1238 {
1239         _regions->block_change_connection (true);
1240         editor_regions_selection_changed_connection.block(true);
1241
1242         if (_region_selection_change_updates_region_list) {
1243                 _regions->unselect_all ();
1244         }
1245
1246         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1247                 (*i)->set_selected_regionviews (selection->regions);
1248         }
1249
1250         if (_region_selection_change_updates_region_list) {
1251                 _regions->set_selected (selection->regions);
1252         }
1253
1254         _regions->block_change_connection (false);
1255         editor_regions_selection_changed_connection.block(false);
1256
1257         if (!_all_region_actions_sensitized) {
1258                 /* This selection change might have changed what region actions
1259                    are allowed, so sensitize them all in case a key is pressed.
1260                 */
1261                 sensitize_all_region_actions (true);
1262         }
1263 }
1264
1265 void
1266 Editor::point_selection_changed ()
1267 {
1268         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1269                 (*i)->set_selected_points (selection->points);
1270         }
1271 }
1272
1273 void
1274 Editor::select_all_in_track (Selection::Operation op)
1275 {
1276         list<Selectable *> touched;
1277
1278         if (!clicked_routeview) {
1279                 return;
1280         }
1281
1282         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1283
1284         switch (op) {
1285         case Selection::Toggle:
1286                 selection->add (touched);
1287                 break;
1288         case Selection::Set:
1289                 selection->set (touched);
1290                 break;
1291         case Selection::Extend:
1292                 /* meaningless, because we're selecting everything */
1293                 break;
1294         case Selection::Add:
1295                 selection->add (touched);
1296                 break;
1297         }
1298 }
1299
1300 void
1301 Editor::select_all_internal_edit (Selection::Operation)
1302 {
1303         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1304                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1305                 if (mrv) {
1306                         mrv->select_all_notes ();
1307                 }
1308         }
1309 }
1310
1311 void
1312 Editor::select_all (Selection::Operation op)
1313 {
1314         list<Selectable *> touched;
1315
1316         if (_internal_editing) {
1317                 select_all_internal_edit (op);
1318                 return;
1319         }
1320
1321         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1322                 if ((*iter)->hidden()) {
1323                         continue;
1324                 }
1325                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1326         }
1327         begin_reversible_command (_("select all"));
1328         switch (op) {
1329         case Selection::Add:
1330                 selection->add (touched);
1331                 break;
1332         case Selection::Toggle:
1333                 selection->add (touched);
1334                 break;
1335         case Selection::Set:
1336                 selection->set (touched);
1337                 break;
1338         case Selection::Extend:
1339                 /* meaningless, because we're selecting everything */
1340                 break;
1341         }
1342         commit_reversible_command ();
1343 }
1344
1345 void
1346 Editor::invert_selection_in_track ()
1347 {
1348         list<Selectable *> touched;
1349
1350         if (!clicked_routeview) {
1351                 return;
1352         }
1353
1354         clicked_routeview->get_inverted_selectables (*selection, touched);
1355         selection->set (touched);
1356 }
1357
1358 void
1359 Editor::invert_selection ()
1360 {
1361         list<Selectable *> touched;
1362
1363         if (_internal_editing) {
1364                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1365                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1366                         if (mrv) {
1367                                 mrv->invert_selection ();
1368                         }
1369                 }
1370                 return;
1371         }
1372
1373         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1374                 if ((*iter)->hidden()) {
1375                         continue;
1376                 }
1377                 (*iter)->get_inverted_selectables (*selection, touched);
1378         }
1379
1380         selection->set (touched);
1381 }
1382
1383 /** @param start Start time in session frames.
1384  *  @param end End time in session frames.
1385  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1386  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1387  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1388  *  within the region are already selected.
1389  */
1390 void
1391 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1392 {
1393         list<Selectable*> found;
1394
1395         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1396
1397                 if ((*iter)->hidden()) {
1398                         continue;
1399                 }
1400
1401                 (*iter)->get_selectables (start, end, top, bot, found);
1402         }
1403
1404         if (found.empty()) {
1405                 return;
1406         }
1407
1408         if (preserve_if_selected && op != Selection::Toggle) {
1409                 list<Selectable*>::iterator i = found.begin();
1410                 while (i != found.end() && (*i)->get_selected()) {
1411                         ++i;
1412                 }
1413
1414                 if (i == found.end()) {
1415                         return;
1416                 }
1417         }
1418
1419         begin_reversible_command (_("select all within"));
1420         switch (op) {
1421         case Selection::Add:
1422                 selection->add (found);
1423                 break;
1424         case Selection::Toggle:
1425                 selection->toggle (found);
1426                 break;
1427         case Selection::Set:
1428                 selection->set (found);
1429                 break;
1430         case Selection::Extend:
1431                 /* not defined yet */
1432                 break;
1433         }
1434
1435         commit_reversible_command ();
1436 }
1437
1438 void
1439 Editor::set_selection_from_region ()
1440 {
1441         if (selection->regions.empty()) {
1442                 return;
1443         }
1444
1445         selection->set (selection->regions.start(), selection->regions.end_frame());
1446         if (!Profile->get_sae()) {
1447                 set_mouse_mode (Editing::MouseRange, false);
1448         }
1449 }
1450
1451 void
1452 Editor::set_selection_from_punch()
1453 {
1454         Location* location;
1455
1456         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1457                 return;
1458         }
1459
1460         set_selection_from_range (*location);
1461 }
1462
1463 void
1464 Editor::set_selection_from_loop()
1465 {
1466         Location* location;
1467
1468         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1469                 return;
1470         }
1471         set_selection_from_range (*location);
1472 }
1473
1474 void
1475 Editor::set_selection_from_range (Location& loc)
1476 {
1477         begin_reversible_command (_("set selection from range"));
1478         selection->set (loc.start(), loc.end());
1479         commit_reversible_command ();
1480
1481         if (!Profile->get_sae()) {
1482                 set_mouse_mode (Editing::MouseRange, false);
1483         }
1484 }
1485
1486 void
1487 Editor::select_all_selectables_using_time_selection ()
1488 {
1489         list<Selectable *> touched;
1490
1491         if (selection->time.empty()) {
1492                 return;
1493         }
1494
1495         framepos_t start = selection->time[clicked_selection].start;
1496         framepos_t end = selection->time[clicked_selection].end;
1497
1498         if (end - start < 1)  {
1499                 return;
1500         }
1501
1502         TrackViewList* ts;
1503
1504         if (selection->tracks.empty()) {
1505                 ts = &track_views;
1506         } else {
1507                 ts = &selection->tracks;
1508         }
1509
1510         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1511                 if ((*iter)->hidden()) {
1512                         continue;
1513                 }
1514                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1515         }
1516
1517         begin_reversible_command (_("select all from range"));
1518         selection->set (touched);
1519         commit_reversible_command ();
1520 }
1521
1522
1523 void
1524 Editor::select_all_selectables_using_punch()
1525 {
1526         Location* location = _session->locations()->auto_punch_location();
1527         list<Selectable *> touched;
1528
1529         if (location == 0 || (location->end() - location->start() <= 1))  {
1530                 return;
1531         }
1532
1533
1534         TrackViewList* ts;
1535
1536         if (selection->tracks.empty()) {
1537                 ts = &track_views;
1538         } else {
1539                 ts = &selection->tracks;
1540         }
1541
1542         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1543                 if ((*iter)->hidden()) {
1544                         continue;
1545                 }
1546                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1547         }
1548         begin_reversible_command (_("select all from punch"));
1549         selection->set (touched);
1550         commit_reversible_command ();
1551
1552 }
1553
1554 void
1555 Editor::select_all_selectables_using_loop()
1556 {
1557         Location* location = _session->locations()->auto_loop_location();
1558         list<Selectable *> touched;
1559
1560         if (location == 0 || (location->end() - location->start() <= 1))  {
1561                 return;
1562         }
1563
1564
1565         TrackViewList* ts;
1566
1567         if (selection->tracks.empty()) {
1568                 ts = &track_views;
1569         } else {
1570                 ts = &selection->tracks;
1571         }
1572
1573         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1574                 if ((*iter)->hidden()) {
1575                         continue;
1576                 }
1577                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1578         }
1579         begin_reversible_command (_("select all from loop"));
1580         selection->set (touched);
1581         commit_reversible_command ();
1582
1583 }
1584
1585 void
1586 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1587 {
1588         framepos_t start;
1589         framepos_t end;
1590         list<Selectable *> touched;
1591
1592         if (after) {
1593                 start = cursor->current_frame;
1594                 end = _session->current_end_frame();
1595         } else {
1596                 if (cursor->current_frame > 0) {
1597                         start = 0;
1598                         end = cursor->current_frame - 1;
1599                 } else {
1600                         return;
1601                 }
1602         }
1603
1604         if (_internal_editing) {
1605                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1606                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1607                         if (mrv) {
1608                                 mrv->select_range (start, end);
1609                         }
1610                 }
1611                 return;
1612         }
1613
1614         if (after) {
1615                 begin_reversible_command (_("select all after cursor"));
1616         } else {
1617                 begin_reversible_command (_("select all before cursor"));
1618         }
1619
1620         TrackViewList* ts;
1621
1622         if (selection->tracks.empty()) {
1623                 ts = &track_views;
1624         } else {
1625                 ts = &selection->tracks;
1626         }
1627
1628         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1629                 if ((*iter)->hidden()) {
1630                         continue;
1631                 }
1632                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1633         }
1634         selection->set (touched);
1635         commit_reversible_command ();
1636 }
1637
1638 void
1639 Editor::select_all_selectables_using_edit (bool after)
1640 {
1641         framepos_t start;
1642         framepos_t end;
1643         list<Selectable *> touched;
1644
1645         if (after) {
1646                 start = get_preferred_edit_position();
1647                 end = _session->current_end_frame();
1648         } else {
1649                 if ((end = get_preferred_edit_position()) > 1) {
1650                         start = 0;
1651                         end -= 1;
1652                 } else {
1653                         return;
1654                 }
1655         }
1656
1657         if (_internal_editing) {
1658                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1659                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1660                         mrv->select_range (start, end);
1661                 }
1662                 return;
1663         }
1664
1665         if (after) {
1666                 begin_reversible_command (_("select all after edit"));
1667         } else {
1668                 begin_reversible_command (_("select all before edit"));
1669         }
1670
1671         TrackViewList* ts;
1672
1673         if (selection->tracks.empty()) {
1674                 ts = &track_views;
1675         } else {
1676                 ts = &selection->tracks;
1677         }
1678
1679         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1680                 if ((*iter)->hidden()) {
1681                         continue;
1682                 }
1683                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1684         }
1685         selection->set (touched);
1686         commit_reversible_command ();
1687 }
1688
1689 void
1690 Editor::select_all_selectables_between (bool /*within*/)
1691 {
1692         framepos_t start;
1693         framepos_t end;
1694         list<Selectable *> touched;
1695
1696         if (!get_edit_op_range (start, end)) {
1697                 return;
1698         }
1699
1700         if (_internal_editing) {
1701                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1702                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1703                         mrv->select_range (start, end);
1704                 }
1705                 return;
1706         }
1707
1708         TrackViewList* ts;
1709
1710         if (selection->tracks.empty()) {
1711                 ts = &track_views;
1712         } else {
1713                 ts = &selection->tracks;
1714         }
1715
1716         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1717                 if ((*iter)->hidden()) {
1718                         continue;
1719                 }
1720                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1721         }
1722
1723         selection->set (touched);
1724 }
1725
1726 void
1727 Editor::select_range_between ()
1728 {
1729         framepos_t start;
1730         framepos_t end;
1731
1732         if (mouse_mode == MouseRange && !selection->time.empty()) {
1733                 selection->clear_time ();
1734         }
1735
1736         if (!get_edit_op_range (start, end)) {
1737                 return;
1738         }
1739
1740         set_mouse_mode (MouseRange);
1741         selection->set (start, end);
1742 }
1743
1744 bool
1745 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1746 {
1747         framepos_t m;
1748         bool ignored;
1749
1750         /* in range mode, use any existing selection */
1751
1752         if (mouse_mode == MouseRange && !selection->time.empty()) {
1753                 /* we know that these are ordered */
1754                 start = selection->time.start();
1755                 end = selection->time.end_frame();
1756                 return true;
1757         }
1758
1759         if (!mouse_frame (m, ignored)) {
1760                 /* mouse is not in a canvas, try playhead+selected marker.
1761                    this is probably most true when using menus.
1762                 */
1763
1764                 if (selection->markers.empty()) {
1765                         return false;
1766                 }
1767
1768                 start = selection->markers.front()->position();
1769                 end = _session->audible_frame();
1770
1771         } else {
1772
1773                 switch (_edit_point) {
1774                 case EditAtPlayhead:
1775                         if (selection->markers.empty()) {
1776                                 /* use mouse + playhead */
1777                                 start = m;
1778                                 end = _session->audible_frame();
1779                         } else {
1780                                 /* use playhead + selected marker */
1781                                 start = _session->audible_frame();
1782                                 end = selection->markers.front()->position();
1783                         }
1784                         break;
1785
1786                 case EditAtMouse:
1787                         /* use mouse + selected marker */
1788                         if (selection->markers.empty()) {
1789                                 start = m;
1790                                 end = _session->audible_frame();
1791                         } else {
1792                                 start = selection->markers.front()->position();
1793                                 end = m;
1794                         }
1795                         break;
1796
1797                 case EditAtSelectedMarker:
1798                         /* use mouse + selected marker */
1799                         if (selection->markers.empty()) {
1800
1801                                 MessageDialog win (_("No edit range defined"),
1802                                                    false,
1803                                                    MESSAGE_INFO,
1804                                                    BUTTONS_OK);
1805
1806                                 win.set_secondary_text (
1807                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1808
1809
1810                                 win.set_default_response (RESPONSE_CLOSE);
1811                                 win.set_position (Gtk::WIN_POS_MOUSE);
1812                                 win.show_all();
1813
1814                                 win.run ();
1815
1816                                 return false; // NO RANGE
1817                         }
1818                         start = selection->markers.front()->position();
1819                         end = m;
1820                         break;
1821                 }
1822         }
1823
1824         if (start == end) {
1825                 return false;
1826         }
1827
1828         if (start > end) {
1829                 swap (start, end);
1830         }
1831
1832         /* turn range into one delimited by start...end,
1833            not start...end-1
1834         */
1835
1836         end++;
1837
1838         return true;
1839 }
1840
1841 void
1842 Editor::deselect_all ()
1843 {
1844         selection->clear ();
1845 }
1846
1847 long
1848 Editor::select_range_around_region (RegionView* rv)
1849 {
1850         assert (rv);
1851
1852         selection->set (&rv->get_time_axis_view());
1853
1854         selection->time.clear ();
1855         boost::shared_ptr<Region> r = rv->region ();
1856         return selection->set (r->position(), r->position() + r->length());
1857 }