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