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