Fix select all before/after edit point.
[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                         } 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         begin_reversible_selection_op(_("Set Selected Track"));
274
275         switch (op) {
276         case Selection::Toggle:
277                 if (selection->selected (&view)) {
278                         if (!no_remove) {
279                                 selection->remove (&view);
280                         }
281                 } else {
282                         selection->add (&view);
283                 }
284                 break;
285
286         case Selection::Add:
287                 if (!selection->selected (&view)) {
288                         selection->add (&view);
289                 }
290                 break;
291
292         case Selection::Set:
293                 selection->set (&view);
294                 break;
295
296         case Selection::Extend:
297                 extend_selection_to_track (view);
298                 break;
299         }
300
301         commit_reversible_selection_op ();
302 }
303
304 void
305 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
306 {
307         if (!clicked_routeview) {
308                 return;
309         }
310
311         if (!press) {
312                 return;
313         }
314
315         set_selected_track (*clicked_routeview, op, no_remove);
316 }
317
318 bool
319 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
320 {
321         if (!clicked_control_point) {
322                 return false;
323         }
324
325         switch (op) {
326         case Selection::Set:
327                 if (press) {
328                         selection->set (clicked_control_point);
329                 }
330                 break;
331         case Selection::Add:
332                 if (press) {
333                         selection->add (clicked_control_point);
334                 }
335                 break;
336         case Selection::Toggle:
337                 /* This is a bit of a hack; if we Primary-Click-Drag a control
338                    point (for push drag) we want the point we clicked on to be
339                    selected, otherwise we end up confusingly dragging an
340                    unselected point.  So here we ensure that the point is selected
341                    after the press, and if we subsequently get a release (meaning no
342                    drag occurred) we set things up so that the toggle has happened.
343                 */
344                 if (press && !selection->selected (clicked_control_point)) {
345                         /* This is the button press, and the control point is not selected; make it so,
346                            in case this press leads to a drag.  Also note that having done this, we don't
347                            need to toggle again on release.
348                         */
349                         selection->toggle (clicked_control_point);
350                         _control_point_toggled_on_press = true;
351                 } else if (!press && !_control_point_toggled_on_press) {
352                         /* This is the release, and the point wasn't toggled on the press, so do it now */
353                         selection->toggle (clicked_control_point);
354                 } else {
355                         /* Reset our flag */
356                         _control_point_toggled_on_press = false;
357                 }
358                 break;
359         case Selection::Extend:
360                 /* XXX */
361                 break;
362         }
363
364         return true;
365 }
366
367 void
368 Editor::get_onscreen_tracks (TrackViewList& tvl)
369 {
370         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
371                 if ((*i)->y_position() < _visible_canvas_height) {
372                         tvl.push_back (*i);
373                 }
374         }
375 }
376
377 /** Call a slot for a given `basis' track and also for any track that is in the same
378  *  active route group with a particular set of properties.
379  *
380  *  @param sl Slot to call.
381  *  @param basis Basis track.
382  *  @param prop Properties that active edit groups must share to be included in the map.
383  */
384
385 void
386 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
387 {
388         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
389
390         if (route_basis == 0) {
391                 return;
392         }
393
394         set<RouteTimeAxisView*> tracks;
395         tracks.insert (route_basis);
396
397         RouteGroup* group = route_basis->route()->route_group();
398
399         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
400
401                 /* the basis is a member of an active route group, with the appropriate
402                    properties; find other members */
403
404                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
405                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
406                         if (v && v->route()->route_group() == group) {
407                                 tracks.insert (v);
408                         }
409                 }
410         }
411
412         /* call the slots */
413         uint32_t const sz = tracks.size ();
414
415         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
416                 sl (**i, sz);
417         }
418 }
419
420 /** Call a slot for a given `basis' track and also for any track that is in the same
421  *  active route group with a particular set of properties.
422  *
423  *  @param sl Slot to call.
424  *  @param basis Basis track.
425  *  @param prop Properties that active edit groups must share to be included in the map.
426  */
427
428 void
429 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
430 {
431         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
432         set<boost::shared_ptr<Playlist> > playlists;
433
434         if (route_basis == 0) {
435                 return;
436         }
437
438         set<RouteTimeAxisView*> tracks;
439         tracks.insert (route_basis);
440
441         RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
442
443         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
444
445                 /* the basis is a member of an active route group, with the appropriate
446                    properties; find other members */
447
448                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
449                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
450
451                         if (v && v->route()->route_group() == group) {
452                                 
453                                 boost::shared_ptr<Track> t = v->track();
454                                 if (t) {
455                                         if (playlists.insert (t->playlist()).second) {
456                                                 /* haven't seen this playlist yet */
457                                                 tracks.insert (v);
458                                         }
459                                 } else {
460                                         /* not actually a "Track", but a timeaxis view that
461                                            we should mapover anyway.
462                                         */
463                                         tracks.insert (v);
464                                 }
465                         }
466                 }
467         }
468
469         /* call the slots */
470         uint32_t const sz = tracks.size ();
471
472         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
473                 sl (**i, sz);
474         }
475 }
476
477 void
478 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
479 {
480         boost::shared_ptr<Playlist> pl;
481         vector<boost::shared_ptr<Region> > results;
482         RegionView* marv;
483         boost::shared_ptr<Track> tr;
484
485         if ((tr = tv.track()) == 0) {
486                 /* bus */
487                 return;
488         }
489
490         if (&tv == &basis->get_time_axis_view()) {
491                 /* looking in same track as the original */
492                 return;
493         }
494
495         if ((pl = tr->playlist()) != 0) {
496                 pl->get_equivalent_regions (basis->region(), results);
497         }
498
499         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
500                 if ((marv = tv.view()->find_view (*ir)) != 0) {
501                         all_equivs->push_back (marv);
502                 }
503         }
504 }
505
506 void
507 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
508 {
509         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);
510
511         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
512
513         equivalent_regions.push_back (basis);
514 }
515
516 RegionSelection
517 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
518 {
519         RegionSelection equivalent;
520
521         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
522
523                 vector<RegionView*> eq;
524
525                 mapover_tracks_with_unique_playlists (
526                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
527                         &(*i)->get_time_axis_view(), prop);
528
529                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
530                         equivalent.add (*j);
531                 }
532
533                 equivalent.add (*i);
534         }
535
536         return equivalent;
537 }
538
539 int
540 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
541 {
542         int region_count = 0;
543
544         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
545
546                 RouteTimeAxisView* tatv;
547
548                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
549
550                         boost::shared_ptr<Playlist> pl;
551                         vector<boost::shared_ptr<Region> > results;
552                         RegionView* marv;
553                         boost::shared_ptr<Track> tr;
554
555                         if ((tr = tatv->track()) == 0) {
556                                 /* bus */
557                                 continue;
558                         }
559
560                         if ((pl = (tr->playlist())) != 0) {
561                                 pl->get_region_list_equivalent_regions (region, results);
562                         }
563
564                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
565                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
566                                         region_count++;
567                                 }
568                         }
569
570                 }
571         }
572
573         return region_count;
574 }
575
576
577 bool
578 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
579 {
580         vector<RegionView*> all_equivalent_regions;
581         bool commit = false;
582
583         if (!clicked_regionview || !clicked_routeview) {
584                 return false;
585         }
586
587         if (press) {
588                 button_release_can_deselect = false;
589         }
590
591         if (op == Selection::Toggle || op == Selection::Set) {
592
593                 switch (op) {
594                 case Selection::Toggle:
595                         if (selection->selected (clicked_regionview)) {
596                                 if (press) {
597
598                                         /* whatever was clicked was selected already; do nothing here but allow
599                                            the button release to deselect it
600                                         */
601
602                                         button_release_can_deselect = true;
603
604                                 } else {
605                                         if (button_release_can_deselect) {
606
607                                                 /* just remove this one region, but only on a permitted button release */
608
609                                                 selection->remove (clicked_regionview);
610                                                 commit = true;
611
612                                                 /* no more deselect action on button release till a new press
613                                                    finds an already selected object.
614                                                 */
615
616                                                 button_release_can_deselect = false;
617                                         }
618                                 }
619
620                         } else {
621
622                                 if (press) {
623
624                                         if (selection->selected (clicked_routeview)) {
625                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
626                                         } else {
627                                                 all_equivalent_regions.push_back (clicked_regionview);
628                                         }
629
630                                         /* add all the equivalent regions, but only on button press */
631
632                                         if (!all_equivalent_regions.empty()) {
633                                                 commit = true;
634                                         }
635
636                                         selection->add (all_equivalent_regions);
637                                 }
638                         }
639                         break;
640
641                 case Selection::Set:
642                         if (!selection->selected (clicked_regionview)) {
643                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
644                                 selection->set (all_equivalent_regions);
645                                 commit = true;
646                         } else {
647                                 /* clicked on an already selected region */
648                                 if (press)
649                                         goto out;
650                                 else {
651                                         get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
652                                         selection->set(all_equivalent_regions);
653                                         commit = true;
654                                 }
655                         }
656                         break;
657
658                 default:
659                         /* silly compiler */
660                         break;
661                 }
662
663         } else if (op == Selection::Extend) {
664
665                 list<Selectable*> results;
666                 framepos_t last_frame;
667                 framepos_t first_frame;
668                 bool same_track = false;
669
670                 /* 1. find the last selected regionview in the track that was clicked in */
671
672                 last_frame = 0;
673                 first_frame = max_framepos;
674
675                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
676                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
677
678                                 if ((*x)->region()->last_frame() > last_frame) {
679                                         last_frame = (*x)->region()->last_frame();
680                                 }
681
682                                 if ((*x)->region()->first_frame() < first_frame) {
683                                         first_frame = (*x)->region()->first_frame();
684                                 }
685
686                                 same_track = true;
687                         }
688                 }
689
690                 if (same_track) {
691
692                         /* 2. figure out the boundaries for our search for new objects */
693
694                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
695                         case Evoral::OverlapNone:
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::OverlapExternal:
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::OverlapInternal:
716                                 if (last_frame < clicked_regionview->region()->first_frame()) {
717                                         first_frame = last_frame;
718                                         last_frame = clicked_regionview->region()->last_frame();
719                                 } else {
720                                         last_frame = first_frame;
721                                         first_frame = clicked_regionview->region()->first_frame();
722                                 }
723                                 break;
724
725                         case Evoral::OverlapStart:
726                         case Evoral::OverlapEnd:
727                                 /* nothing to do except add clicked region to selection, since it
728                                    overlaps with the existing selection in this track.
729                                 */
730                                 break;
731                         }
732
733                 } else {
734
735                         /* click in a track that has no regions selected, so extend vertically
736                            to pick out all regions that are defined by the existing selection
737                            plus this one.
738                         */
739
740
741                         first_frame = clicked_regionview->region()->position();
742                         last_frame = clicked_regionview->region()->last_frame();
743
744                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
745                                 if ((*i)->region()->position() < first_frame) {
746                                         first_frame = (*i)->region()->position();
747                                 }
748                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
749                                         last_frame = (*i)->region()->last_frame();
750                                 }
751                         }
752                 }
753
754                 /* 2. find all the tracks we should select in */
755
756                 set<RouteTimeAxisView*> relevant_tracks;
757
758                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
759                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
760                         if (r) {
761                                 relevant_tracks.insert (r);
762                         }
763                 }
764
765                 set<RouteTimeAxisView*> already_in_selection;
766
767                 if (relevant_tracks.empty()) {
768
769                         /* no tracks selected .. thus .. if the
770                            regionview we're in isn't selected
771                            (i.e. we're about to extend to it), then
772                            find all tracks between the this one and
773                            any selected ones.
774                         */
775
776                         if (!selection->selected (clicked_regionview)) {
777
778                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
779
780                                 if (rtv) {
781
782                                         /* add this track to the ones we will search */
783
784                                         relevant_tracks.insert (rtv);
785
786                                         /* find the track closest to this one that
787                                            already a selected region.
788                                         */
789
790                                         RouteTimeAxisView* closest = 0;
791                                         int distance = INT_MAX;
792                                         int key = rtv->route()->order_key ();
793
794                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
795
796                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
797
798                                                 if (artv && artv != rtv) {
799
800                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
801
802                                                         result = already_in_selection.insert (artv);
803
804                                                         if (result.second) {
805                                                                 /* newly added to already_in_selection */
806
807                                                                 int d = artv->route()->order_key ();
808
809                                                                 d -= key;
810
811                                                                 if (abs (d) < distance) {
812                                                                         distance = abs (d);
813                                                                         closest = artv;
814                                                                 }
815                                                         }
816                                                 }
817                                         }
818
819                                         if (closest) {
820
821                                                 /* now add all tracks between that one and this one */
822
823                                                 int okey = closest->route()->order_key ();
824
825                                                 if (okey > key) {
826                                                         swap (okey, key);
827                                                 }
828
829                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
830                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
831                                                         if (artv && artv != rtv) {
832
833                                                                 int k = artv->route()->order_key ();
834
835                                                                 if (k >= okey && k <= key) {
836
837                                                                         /* in range but don't add it if
838                                                                            it already has tracks selected.
839                                                                            this avoids odd selection
840                                                                            behaviour that feels wrong.
841                                                                         */
842
843                                                                         if (find (already_in_selection.begin(),
844                                                                                   already_in_selection.end(),
845                                                                                   artv) == already_in_selection.end()) {
846
847                                                                                 relevant_tracks.insert (artv);
848                                                                         }
849                                                                 }
850                                                         }
851                                                 }
852                                         }
853                                 }
854                         }
855                 }
856
857                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
858                    one that was clicked.
859                 */
860
861                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
862                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
863                 }
864
865                 /* 4. convert to a vector of regions */
866
867                 vector<RegionView*> regions;
868
869                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
870                         RegionView* arv;
871
872                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
873                                 regions.push_back (arv);
874                         }
875                 }
876
877                 if (!regions.empty()) {
878                         selection->add (regions);
879                         commit = true;
880                 }
881         }
882
883 out:
884         return commit;
885 }
886
887
888 void
889 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
890 {
891         vector<RegionView*> all_equivalent_regions;
892
893         get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
894
895         if (all_equivalent_regions.empty()) {
896                 return;
897         }
898
899         begin_reversible_selection_op (_("set selected regions"));
900
901         switch (op) {
902         case Selection::Toggle:
903                 /* XXX this is not correct */
904                 selection->toggle (all_equivalent_regions);
905                 break;
906         case Selection::Set:
907                 selection->set (all_equivalent_regions);
908                 break;
909         case Selection::Extend:
910                 selection->add (all_equivalent_regions);
911                 break;
912         case Selection::Add:
913                 selection->add (all_equivalent_regions);
914                 break;
915         }
916
917         commit_reversible_selection_op () ;
918 }
919
920 bool
921 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
922 {
923         RegionView* rv;
924         boost::shared_ptr<Region> r (weak_r.lock());
925
926         if (!r) {
927                 return true;
928         }
929
930         if ((rv = sv->find_view (r)) == 0) {
931                 return true;
932         }
933
934         /* don't reset the selection if its something other than
935            a single other region.
936         */
937
938         if (selection->regions.size() > 1) {
939                 return true;
940         }
941
942         begin_reversible_selection_op (_("set selected regions"));
943
944         selection->set (rv);
945
946         commit_reversible_selection_op () ;
947
948         return true;
949 }
950
951 void
952 Editor::track_selection_changed ()
953 {
954         switch (selection->tracks.size()) {
955         case 0:
956                 break;
957         default:
958                 set_selected_mixer_strip (*(selection->tracks.front()));
959                 break;
960         }
961
962         RouteNotificationListPtr routes (new RouteNotificationList);
963
964         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
965
966                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
967
968                 (*i)->set_selected (yn);
969
970                 TimeAxisView::Children c = (*i)->get_child_list ();
971                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
972                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
973                 }
974
975                 if (yn) {
976                         (*i)->reshow_selection (selection->time);
977                 } else {
978                         (*i)->hide_selection ();
979                 }
980
981
982                 if (yn) {
983                         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
984                         if (rtav) {
985                                 routes->push_back (rtav->route());
986                         }
987                 }
988         }
989
990         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
991
992         /* notify control protocols */
993         
994         ControlProtocol::TrackSelectionChanged (routes);
995 }
996
997 void
998 Editor::time_selection_changed ()
999 {
1000         if (Profile->get_sae()) {
1001                 return;
1002         }
1003
1004         /* XXX this is superficially inefficient. Hide the selection in all
1005          * tracks, then show it in all selected tracks.
1006          *
1007          * However, if you investigate what this actually does, it isn't
1008          * anywhere nearly as bad as it may appear. Remember: nothing is
1009          * redrawn or even recomputed during these two loops - that only
1010          * happens when we next render ...
1011          */
1012
1013         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1014                 (*i)->hide_selection ();
1015         }
1016
1017         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1018                 (*i)->show_selection (selection->time);
1019         }
1020
1021         if (selection->time.empty()) {
1022                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1023         } else {
1024                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1025         }
1026 }
1027
1028 /** Set all region actions to have a given sensitivity */
1029 void
1030 Editor::sensitize_all_region_actions (bool s)
1031 {
1032         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1033
1034         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1035                 (*i)->set_sensitive (s);
1036         }
1037
1038         _all_region_actions_sensitized = s;
1039 }
1040
1041 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1042  *  This method should be called just before displaying a Region menu.  When a Region menu is not
1043  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
1044  *  on entered_regionviews work without having to check sensitivity every time the selection or
1045  *  entered_regionview changes.
1046  *
1047  *  This method also sets up toggle action state as appropriate.
1048  */
1049 void
1050 Editor::sensitize_the_right_region_actions ()
1051 {
1052
1053         RegionSelection rs = get_regions_from_selection_and_entered ();
1054         sensitize_all_region_actions (!rs.empty ());
1055
1056         _ignore_region_action = true;
1057
1058         /* Look through the regions that are selected and make notes about what we have got */
1059
1060         bool have_audio = false;
1061         bool have_multichannel_audio = false;
1062         bool have_midi = false;
1063         bool have_locked = false;
1064         bool have_unlocked = false;
1065         bool have_video_locked = false;
1066         bool have_video_unlocked = false;
1067         bool have_position_lock_style_audio = false;
1068         bool have_position_lock_style_music = false;
1069         bool have_muted = false;
1070         bool have_unmuted = false;
1071         bool have_opaque = false;
1072         bool have_non_opaque = false;
1073         bool have_not_at_natural_position = false;
1074         bool have_envelope_active = false;
1075         bool have_envelope_inactive = false;
1076         bool have_non_unity_scale_amplitude = false;
1077         bool have_compound_regions = false;
1078         bool have_inactive_fade_in = false;
1079         bool have_inactive_fade_out = false;
1080         bool have_active_fade_in = false;
1081         bool have_active_fade_out = false;
1082
1083         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1084
1085                 boost::shared_ptr<Region> r = (*i)->region ();
1086                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1087
1088                 if (ar) {
1089                         have_audio = true;
1090                         if (ar->n_channels() > 1) {
1091                                 have_multichannel_audio = true;
1092                         }
1093                 }
1094
1095                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1096                         have_midi = true;
1097                 }
1098
1099                 if (r->is_compound()) {
1100                         have_compound_regions = true;
1101                 }
1102
1103                 if (r->locked()) {
1104                         have_locked = true;
1105                 } else {
1106                         have_unlocked = true;
1107                 }
1108
1109                 if (r->video_locked()) {
1110                         have_video_locked = true;
1111                 } else {
1112                         have_video_unlocked = true;
1113                 }
1114
1115                 if (r->position_lock_style() == MusicTime) {
1116                         have_position_lock_style_music = true;
1117                 } else {
1118                         have_position_lock_style_audio = true;
1119                 }
1120
1121                 if (r->muted()) {
1122                         have_muted = true;
1123                 } else {
1124                         have_unmuted = true;
1125                 }
1126
1127                 if (r->opaque()) {
1128                         have_opaque = true;
1129                 } else {
1130                         have_non_opaque = true;
1131                 }
1132
1133                 if (!r->at_natural_position()) {
1134                         have_not_at_natural_position = true;
1135                 }
1136
1137                 if (ar) {
1138                         if (ar->envelope_active()) {
1139                                 have_envelope_active = true;
1140                         } else {
1141                                 have_envelope_inactive = true;
1142                         }
1143
1144                         if (ar->scale_amplitude() != 1) {
1145                                 have_non_unity_scale_amplitude = true;
1146                         }
1147
1148                         if (ar->fade_in_active ()) {
1149                                 have_active_fade_in = true;
1150                         } else {
1151                                 have_inactive_fade_in = true;
1152                         }
1153
1154                         if (ar->fade_out_active ()) {
1155                                 have_active_fade_out = true;
1156                         } else {
1157                                 have_inactive_fade_out = true;
1158                         }
1159                 }
1160         }
1161
1162         if (rs.size() > 1) {
1163                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1164                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1165                 _region_actions->get_action("rename-region")->set_sensitive (false);
1166                 if (have_audio) {
1167                         /* XXX need to check whether there is than 1 per
1168                            playlist, because otherwise this makes no sense.
1169                         */
1170                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1171                 } else {
1172                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1173                 }
1174         } else if (rs.size() == 1) {
1175                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1176                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1177                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1178         }
1179
1180         if (!have_multichannel_audio) {
1181                 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1182         }
1183
1184         if (!have_midi) {
1185                 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1186                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1187                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1188                 _region_actions->get_action("legatize-region")->set_sensitive (false);
1189                 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1190                 _region_actions->get_action("fork-region")->set_sensitive (false);
1191                 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1192                 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1193                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1194         } else {
1195                 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1196                 /* others were already marked sensitive */
1197         }
1198
1199         if (_edit_point == EditAtMouse) {
1200                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1201                 _region_actions->get_action("trim-front")->set_sensitive (false);
1202                 _region_actions->get_action("trim-back")->set_sensitive (false);
1203                 _region_actions->get_action("split-region")->set_sensitive (false);
1204                 _region_actions->get_action("place-transient")->set_sensitive (false);
1205         }
1206
1207         if (have_compound_regions) {
1208                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1209         } else {
1210                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1211         }
1212
1213         if (have_audio) {
1214
1215                 if (have_envelope_active && !have_envelope_inactive) {
1216                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1217                 } else if (have_envelope_active && have_envelope_inactive) {
1218                         // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1219                 }
1220
1221         } else {
1222
1223                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1224                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1225                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1226                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1227
1228         }
1229
1230         if (!have_non_unity_scale_amplitude || !have_audio) {
1231                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1232         }
1233
1234         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1235         a->set_active (have_locked && !have_unlocked);
1236         if (have_locked && have_unlocked) {
1237                 // a->set_inconsistent ();
1238         }
1239
1240         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1241         a->set_active (have_video_locked && !have_video_unlocked);
1242         if (have_video_locked && have_video_unlocked) {
1243                 // a->set_inconsistent ();
1244         }
1245
1246         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1247         a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1248
1249         if (have_position_lock_style_music && have_position_lock_style_audio) {
1250                 // a->set_inconsistent ();
1251         }
1252
1253         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1254         a->set_active (have_muted && !have_unmuted);
1255         if (have_muted && have_unmuted) {
1256                 // a->set_inconsistent ();
1257         }
1258
1259         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1260         a->set_active (have_opaque && !have_non_opaque);
1261         if (have_opaque && have_non_opaque) {
1262                 // a->set_inconsistent ();
1263         }
1264
1265         if (!have_not_at_natural_position) {
1266                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1267         }
1268
1269         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1270         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1271                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1272         } else {
1273                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1274         }
1275
1276         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1277         a->set_active (have_active_fade_in && !have_inactive_fade_in);
1278         if (have_active_fade_in && have_inactive_fade_in) {
1279                 // a->set_inconsistent ();
1280         }
1281
1282         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1283         a->set_active (have_active_fade_out && !have_inactive_fade_out);
1284
1285         if (have_active_fade_out && have_inactive_fade_out) {
1286                 // a->set_inconsistent ();
1287         }
1288         
1289         bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1290         bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1291
1292         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1293         a->set_active (have_active_fade && !have_inactive_fade);
1294
1295         if (have_active_fade && have_inactive_fade) {
1296                 // a->set_inconsistent ();
1297         }
1298         
1299         _ignore_region_action = false;
1300
1301         _all_region_actions_sensitized = false;
1302 }
1303
1304
1305 void
1306 Editor::region_selection_changed ()
1307 {
1308         _regions->block_change_connection (true);
1309         editor_regions_selection_changed_connection.block(true);
1310
1311         if (_region_selection_change_updates_region_list) {
1312                 _regions->unselect_all ();
1313         }
1314
1315         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1316                 (*i)->set_selected_regionviews (selection->regions);
1317         }
1318
1319         if (_region_selection_change_updates_region_list) {
1320                 _regions->set_selected (selection->regions);
1321         }
1322
1323         _regions->block_change_connection (false);
1324         editor_regions_selection_changed_connection.block(false);
1325
1326         if (!_all_region_actions_sensitized) {
1327                 /* This selection change might have changed what region actions
1328                    are allowed, so sensitize them all in case a key is pressed.
1329                 */
1330                 sensitize_all_region_actions (true);
1331         }
1332
1333         if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1334                 maybe_locate_with_edit_preroll (selection->regions.start());
1335         }
1336 }
1337
1338 void
1339 Editor::point_selection_changed ()
1340 {
1341         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1342                 (*i)->set_selected_points (selection->points);
1343         }
1344 }
1345
1346 void
1347 Editor::select_all_in_track (Selection::Operation op)
1348 {
1349         list<Selectable *> touched;
1350
1351         if (!clicked_routeview) {
1352                 return;
1353         }
1354
1355         begin_reversible_selection_op(_("Select All in Track"));
1356
1357         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1358
1359         switch (op) {
1360         case Selection::Toggle:
1361                 selection->add (touched);
1362                 break;
1363         case Selection::Set:
1364                 selection->set (touched);
1365                 break;
1366         case Selection::Extend:
1367                 /* meaningless, because we're selecting everything */
1368                 break;
1369         case Selection::Add:
1370                 selection->add (touched);
1371                 break;
1372         }
1373
1374         commit_reversible_selection_op ();
1375 }
1376
1377 bool
1378 Editor::select_all_internal_edit (Selection::Operation)
1379 {
1380         bool selected = false;
1381
1382         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1383                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1384                 if (mrv) {
1385                         mrv->select_all_notes ();
1386                         selected = true;
1387                 }
1388         }
1389
1390         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1391         if (mrv) {
1392                 mrv->select_all_notes ();
1393                 selected = true;
1394         }
1395
1396         return selected;
1397 }
1398
1399 void
1400 Editor::select_all_objects (Selection::Operation op)
1401 {
1402         list<Selectable *> touched;
1403
1404         TrackViewList ts  = track_views;
1405
1406         if (internal_editing() && select_all_internal_edit(op)) {
1407                 return;  // Selected notes
1408         }
1409
1410         for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1411                 if ((*iter)->hidden()) {
1412                         continue;
1413                 }
1414                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1415                 selection->add (*iter);
1416         }
1417
1418
1419         begin_reversible_selection_op (_("select all"));
1420         switch (op) {
1421         case Selection::Add:
1422                 selection->add (touched);
1423                 break;
1424         case Selection::Toggle:
1425                 selection->add (touched);
1426                 break;
1427         case Selection::Set:
1428                 selection->set (touched);
1429                 break;
1430         case Selection::Extend:
1431                 /* meaningless, because we're selecting everything */
1432                 break;
1433         }
1434         commit_reversible_selection_op ();
1435 }
1436
1437 void
1438 Editor::invert_selection_in_track ()
1439 {
1440         list<Selectable *> touched;
1441
1442         if (!clicked_routeview) {
1443                 return;
1444         }
1445
1446         begin_reversible_selection_op(_("Invert Selection in Track"));
1447         clicked_routeview->get_inverted_selectables (*selection, touched);
1448         selection->set (touched);
1449         commit_reversible_selection_op ();
1450 }
1451
1452 void
1453 Editor::invert_selection ()
1454 {
1455         list<Selectable *> touched;
1456
1457         if (internal_editing()) {
1458                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1459                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1460                         if (mrv) {
1461                                 mrv->invert_selection ();
1462                         }
1463                 }
1464                 return;
1465         }
1466
1467         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1468                 if ((*iter)->hidden()) {
1469                         continue;
1470                 }
1471                 (*iter)->get_inverted_selectables (*selection, touched);
1472         }
1473
1474         begin_reversible_selection_op(_("Invert Selection"));
1475         selection->set (touched);
1476         commit_reversible_selection_op ();
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_selection_op (_("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_selection_op ();
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_selection_op (_("set selection from range"));
1576         selection->set (loc.start(), loc.end());
1577         commit_reversible_selection_op ();
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_selection_op (_("select all from range"));
1616         selection->set (touched);
1617         commit_reversible_selection_op ();
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_selection_op (_("select all from punch"));
1647         selection->set (touched);
1648         commit_reversible_selection_op ();
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_selection_op (_("select all from loop"));
1678         selection->set (touched);
1679         commit_reversible_selection_op ();
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_selection_op (_("select all after cursor"));
1714         } else {
1715                 begin_reversible_selection_op (_("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_selection_op ();
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(false, true);
1745                 end = _session->current_end_frame();
1746         } else {
1747                 if ((end = get_preferred_edit_position(false, true)) > 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_selection_op (_("select all after edit"));
1765         } else {
1766                 begin_reversible_selection_op (_("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_selection_op ();
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         begin_reversible_selection_op(_("Select all Selectables Between"));
1822         selection->set (touched);
1823         commit_reversible_selection_op ();
1824 }
1825
1826 void
1827 Editor::select_range_between ()
1828 {
1829         framepos_t start;
1830         framepos_t end;
1831
1832         if ( !selection->time.empty() ) {
1833                 selection->clear_time ();
1834         }
1835
1836         if (!get_edit_op_range (start, end)) {
1837                 return;
1838         }
1839
1840         begin_reversible_selection_op(_("Select Range Between"));
1841         set_mouse_mode (MouseRange);
1842         selection->set (start, end);
1843         commit_reversible_selection_op ();
1844 }
1845
1846 bool
1847 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1848 {
1849 //      framepos_t m;
1850 //      bool ignored;
1851
1852         /* if an explicit range exists, use it */
1853
1854         if ( (mouse_mode == MouseRange || get_smart_mode() ) &&  !selection->time.empty()) {
1855                 /* we know that these are ordered */
1856                 start = selection->time.start();
1857                 end = selection->time.end_frame();
1858                 return true;
1859         } else {
1860                 start = 0;
1861                 end = 0;
1862                 return false;
1863         }
1864         
1865 //      if (!mouse_frame (m, ignored)) {
1866 //              /* mouse is not in a canvas, try playhead+selected marker.
1867 //                 this is probably most true when using menus.
1868 //              */
1869 //
1870 //              if (selection->markers.empty()) {
1871 //                      return false;
1872 //              }
1873
1874 //              start = selection->markers.front()->position();
1875 //              end = _session->audible_frame();
1876
1877 //      } else {
1878
1879 //              switch (_edit_point) {
1880 //              case EditAtPlayhead:
1881 //                      if (selection->markers.empty()) {
1882 //                              /* use mouse + playhead */
1883 //                              start = m;
1884 //                              end = _session->audible_frame();
1885 //                      } else {
1886 //                              /* use playhead + selected marker */
1887 //                              start = _session->audible_frame();
1888 //                              end = selection->markers.front()->position();
1889 //                      }
1890 //                      break;
1891
1892 //              case EditAtMouse:
1893 //                      /* use mouse + selected marker */
1894 //                      if (selection->markers.empty()) {
1895 //                              start = m;
1896 //                              end = _session->audible_frame();
1897 //                      } else {
1898 //                              start = selection->markers.front()->position();
1899 //                              end = m;
1900 //                      }
1901 //                      break;
1902
1903 //              case EditAtSelectedMarker:
1904 //                      /* use mouse + selected marker */
1905 //                      if (selection->markers.empty()) {
1906
1907 //                              MessageDialog win (_("No edit range defined"),
1908 //                                                 false,
1909 //                                                 MESSAGE_INFO,
1910 //                                                 BUTTONS_OK);
1911
1912 //                              win.set_secondary_text (
1913 //                                      _("the edit point is Selected Marker\nbut there is no selected marker."));
1914
1915
1916 //                              win.set_default_response (RESPONSE_CLOSE);
1917 //                              win.set_position (Gtk::WIN_POS_MOUSE);
1918 //                              win.show_all();
1919
1920 //                              win.run ();
1921
1922 //                              return false; // NO RANGE
1923 //                      }
1924 //                      start = selection->markers.front()->position();
1925 //                      end = m;
1926 //                      break;
1927 //              }
1928 //      }
1929
1930 //      if (start == end) {
1931 //              return false;
1932 //      }
1933
1934 //      if (start > end) {
1935 //              swap (start, end);
1936 //      }
1937
1938         /* turn range into one delimited by start...end,
1939            not start...end-1
1940         */
1941
1942 //      end++;
1943
1944 //      return true;
1945 }
1946
1947 void
1948 Editor::deselect_all ()
1949 {
1950         begin_reversible_selection_op(_("Clear Selection"));
1951         selection->clear ();
1952         commit_reversible_selection_op ();
1953 }
1954
1955 long
1956 Editor::select_range (framepos_t s, framepos_t e)
1957 {
1958         begin_reversible_selection_op(_("Select Range"));
1959         selection->add (clicked_axisview);
1960         selection->time.clear ();
1961         long ret = selection->set (s, e);
1962         commit_reversible_selection_op ();
1963         return ret;
1964 }