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