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