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