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