Make crossfade convert-to-full and activate/deactivate work across edit groups (...
[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_visible = false;
1075         bool have_envelope_invisible = false;
1076         bool have_envelope_active = false;
1077         bool have_envelope_inactive = false;
1078         bool have_non_unity_scale_amplitude = false;
1079         bool have_compound_regions = false;
1080
1081         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1082
1083                 boost::shared_ptr<Region> r = (*i)->region ();
1084                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1085
1086                 if (ar) {
1087                         have_audio = true;
1088                         if (ar->n_channels() > 1) {
1089                                 have_multichannel_audio = true;
1090                         }
1091                 }
1092
1093                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1094                         have_midi = true;
1095                 }
1096
1097                 if (r->is_compound()) {
1098                         have_compound_regions = true;
1099                 }
1100
1101                 if (r->locked()) {
1102                         have_locked = true;
1103                 } else {
1104                         have_unlocked = true;
1105                 }
1106
1107                 if (r->position_lock_style() == MusicTime) {
1108                         have_position_lock_style_music = true;
1109                 } else {
1110                         have_position_lock_style_audio = true;
1111                 }
1112
1113                 if (r->muted()) {
1114                         have_muted = true;
1115                 } else {
1116                         have_unmuted = true;
1117                 }
1118
1119                 if (r->opaque()) {
1120                         have_opaque = true;
1121                 } else {
1122                         have_non_opaque = true;
1123                 }
1124
1125                 if (!r->at_natural_position()) {
1126                         have_not_at_natural_position = true;
1127                 }
1128
1129                 if (ar) {
1130                         /* its a bit unfortunate that "envelope visible" is a view-only
1131                            property. we have to find the regionview to able to check
1132                            its current setting.
1133                         */
1134
1135                         have_envelope_invisible = false;
1136
1137                         if (*i) {
1138                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1139                                 if (arv) {
1140                                         if (arv->envelope_visible()) {
1141                                                 have_envelope_visible = true;
1142                                         } else {
1143                                                 have_envelope_invisible = true;
1144                                         }
1145                                 }
1146                         }
1147
1148                         if (ar->envelope_active()) {
1149                                 have_envelope_active = true;
1150                         } else {
1151                                 have_envelope_inactive = true;
1152                         }
1153
1154                         if (ar->scale_amplitude() != 1) {
1155                                 have_non_unity_scale_amplitude = true;
1156                         }
1157                 }
1158         }
1159
1160         if (rs.size() > 1) {
1161                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1162                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1163                 _region_actions->get_action("rename-region")->set_sensitive (false);
1164                 if (have_audio) {
1165                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1166                 } else {
1167                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1168                 }
1169         } else if (rs.size() == 1) {
1170                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1171                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1172                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1173         }
1174
1175         if (!have_multichannel_audio) {
1176                 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1177         }
1178
1179         if (!have_midi) {
1180                 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1181                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1182                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1183                 _region_actions->get_action("fork-region")->set_sensitive (false);
1184                 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1185                 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1186                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1187         } else {
1188                 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1189                 /* others were already marked sensitive */
1190         }
1191
1192         if (_edit_point == EditAtMouse) {
1193                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1194                 _region_actions->get_action("trim-front")->set_sensitive (false);
1195                 _region_actions->get_action("trim-back")->set_sensitive (false);
1196                 _region_actions->get_action("split-region")->set_sensitive (false);
1197                 _region_actions->get_action("place-transient")->set_sensitive (false);
1198         }
1199
1200         if (have_compound_regions) {
1201                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1202         } else {
1203                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1204         }
1205
1206         if (have_audio) {
1207
1208                 if (have_envelope_visible && !have_envelope_invisible) {
1209                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1210                 } else if (have_envelope_visible && have_envelope_invisible) {
1211                         // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1212                 }
1213
1214                 if (have_envelope_active && !have_envelope_inactive) {
1215                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1216                 } else if (have_envelope_active && have_envelope_inactive) {
1217                         // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1218                 }
1219
1220         } else {
1221
1222                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1223                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1224                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1225                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1226                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1227
1228         }
1229
1230         if (!have_non_unity_scale_amplitude || !have_audio) {
1231                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1232         }
1233
1234         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1235         if (have_locked && have_unlocked) {
1236                 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1237         }
1238
1239         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);
1240
1241         if (have_position_lock_style_music && have_position_lock_style_audio) {
1242                 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1243         }
1244
1245         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1246         if (have_muted && have_unmuted) {
1247                 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1248         }
1249
1250         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1251         if (have_opaque && have_non_opaque) {
1252                 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1253         }
1254
1255         if (!have_not_at_natural_position) {
1256                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1257         }
1258
1259         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1260         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1261                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1262         } else {
1263                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1264         }
1265
1266         _ignore_region_action = false;
1267
1268         _all_region_actions_sensitized = false;
1269 }
1270
1271
1272 void
1273 Editor::region_selection_changed ()
1274 {
1275         _regions->block_change_connection (true);
1276         editor_regions_selection_changed_connection.block(true);
1277
1278         if (_region_selection_change_updates_region_list) {
1279                 _regions->unselect_all ();
1280         }
1281
1282         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1283                 (*i)->set_selected_regionviews (selection->regions);
1284         }
1285
1286         if (_region_selection_change_updates_region_list) {
1287                 _regions->set_selected (selection->regions);
1288         }
1289
1290         _regions->block_change_connection (false);
1291         editor_regions_selection_changed_connection.block(false);
1292
1293         if (!_all_region_actions_sensitized) {
1294                 /* This selection change might have changed what region actions
1295                    are allowed, so sensitize them all in case a key is pressed.
1296                 */
1297                 sensitize_all_region_actions (true);
1298         }
1299
1300         if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1301                 _session->request_locate (selection->regions.start());
1302         }
1303 }
1304
1305 void
1306 Editor::point_selection_changed ()
1307 {
1308         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1309                 (*i)->set_selected_points (selection->points);
1310         }
1311 }
1312
1313 void
1314 Editor::select_all_in_track (Selection::Operation op)
1315 {
1316         list<Selectable *> touched;
1317
1318         if (!clicked_routeview) {
1319                 return;
1320         }
1321
1322         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1323
1324         switch (op) {
1325         case Selection::Toggle:
1326                 selection->add (touched);
1327                 break;
1328         case Selection::Set:
1329                 selection->set (touched);
1330                 break;
1331         case Selection::Extend:
1332                 /* meaningless, because we're selecting everything */
1333                 break;
1334         case Selection::Add:
1335                 selection->add (touched);
1336                 break;
1337         }
1338 }
1339
1340 void
1341 Editor::select_all_internal_edit (Selection::Operation)
1342 {
1343         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1344                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1345                 if (mrv) {
1346                         mrv->select_all_notes ();
1347                 }
1348         }
1349 }
1350
1351 void
1352 Editor::select_all (Selection::Operation op)
1353 {
1354         list<Selectable *> touched;
1355
1356         if (_internal_editing) {
1357                 select_all_internal_edit (op);
1358                 return;
1359         }
1360
1361         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1362                 if ((*iter)->hidden()) {
1363                         continue;
1364                 }
1365                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1366         }
1367         begin_reversible_command (_("select all"));
1368         switch (op) {
1369         case Selection::Add:
1370                 selection->add (touched);
1371                 break;
1372         case Selection::Toggle:
1373                 selection->add (touched);
1374                 break;
1375         case Selection::Set:
1376                 selection->set (touched);
1377                 break;
1378         case Selection::Extend:
1379                 /* meaningless, because we're selecting everything */
1380                 break;
1381         }
1382         commit_reversible_command ();
1383 }
1384
1385 void
1386 Editor::invert_selection_in_track ()
1387 {
1388         list<Selectable *> touched;
1389
1390         if (!clicked_routeview) {
1391                 return;
1392         }
1393
1394         clicked_routeview->get_inverted_selectables (*selection, touched);
1395         selection->set (touched);
1396 }
1397
1398 void
1399 Editor::invert_selection ()
1400 {
1401         list<Selectable *> touched;
1402
1403         if (_internal_editing) {
1404                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1405                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1406                         if (mrv) {
1407                                 mrv->invert_selection ();
1408                         }
1409                 }
1410                 return;
1411         }
1412
1413         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1414                 if ((*iter)->hidden()) {
1415                         continue;
1416                 }
1417                 (*iter)->get_inverted_selectables (*selection, touched);
1418         }
1419
1420         selection->set (touched);
1421 }
1422
1423 /** @param start Start time in session frames.
1424  *  @param end End time in session frames.
1425  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1426  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1427  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1428  *  within the region are already selected.
1429  */
1430 void
1431 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1432 {
1433         list<Selectable*> found;
1434
1435         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1436
1437                 if ((*iter)->hidden()) {
1438                         continue;
1439                 }
1440
1441                 (*iter)->get_selectables (start, end, top, bot, found);
1442         }
1443
1444         if (found.empty()) {
1445                 return;
1446         }
1447
1448         if (preserve_if_selected && op != Selection::Toggle) {
1449                 list<Selectable*>::iterator i = found.begin();
1450                 while (i != found.end() && (*i)->get_selected()) {
1451                         ++i;
1452                 }
1453
1454                 if (i == found.end()) {
1455                         return;
1456                 }
1457         }
1458
1459         begin_reversible_command (_("select all within"));
1460         switch (op) {
1461         case Selection::Add:
1462                 selection->add (found);
1463                 break;
1464         case Selection::Toggle:
1465                 selection->toggle (found);
1466                 break;
1467         case Selection::Set:
1468                 selection->set (found);
1469                 break;
1470         case Selection::Extend:
1471                 /* not defined yet */
1472                 break;
1473         }
1474
1475         commit_reversible_command ();
1476 }
1477
1478 void
1479 Editor::set_selection_from_region ()
1480 {
1481         if (selection->regions.empty()) {
1482                 return;
1483         }
1484
1485         selection->set (selection->regions.start(), selection->regions.end_frame());
1486         if (!Profile->get_sae()) {
1487                 set_mouse_mode (Editing::MouseRange, false);
1488         }
1489 }
1490
1491 void
1492 Editor::set_selection_from_punch()
1493 {
1494         Location* location;
1495
1496         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1497                 return;
1498         }
1499
1500         set_selection_from_range (*location);
1501 }
1502
1503 void
1504 Editor::set_selection_from_loop()
1505 {
1506         Location* location;
1507
1508         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1509                 return;
1510         }
1511         set_selection_from_range (*location);
1512 }
1513
1514 void
1515 Editor::set_selection_from_range (Location& loc)
1516 {
1517         begin_reversible_command (_("set selection from range"));
1518         selection->set (loc.start(), loc.end());
1519         commit_reversible_command ();
1520
1521         if (!Profile->get_sae()) {
1522                 set_mouse_mode (Editing::MouseRange, false);
1523         }
1524 }
1525
1526 void
1527 Editor::select_all_selectables_using_time_selection ()
1528 {
1529         list<Selectable *> touched;
1530
1531         if (selection->time.empty()) {
1532                 return;
1533         }
1534
1535         framepos_t start = selection->time[clicked_selection].start;
1536         framepos_t end = selection->time[clicked_selection].end;
1537
1538         if (end - start < 1)  {
1539                 return;
1540         }
1541
1542         TrackViewList* ts;
1543
1544         if (selection->tracks.empty()) {
1545                 ts = &track_views;
1546         } else {
1547                 ts = &selection->tracks;
1548         }
1549
1550         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1551                 if ((*iter)->hidden()) {
1552                         continue;
1553                 }
1554                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1555         }
1556
1557         begin_reversible_command (_("select all from range"));
1558         selection->set (touched);
1559         commit_reversible_command ();
1560 }
1561
1562
1563 void
1564 Editor::select_all_selectables_using_punch()
1565 {
1566         Location* location = _session->locations()->auto_punch_location();
1567         list<Selectable *> touched;
1568
1569         if (location == 0 || (location->end() - location->start() <= 1))  {
1570                 return;
1571         }
1572
1573
1574         TrackViewList* ts;
1575
1576         if (selection->tracks.empty()) {
1577                 ts = &track_views;
1578         } else {
1579                 ts = &selection->tracks;
1580         }
1581
1582         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1583                 if ((*iter)->hidden()) {
1584                         continue;
1585                 }
1586                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1587         }
1588         begin_reversible_command (_("select all from punch"));
1589         selection->set (touched);
1590         commit_reversible_command ();
1591
1592 }
1593
1594 void
1595 Editor::select_all_selectables_using_loop()
1596 {
1597         Location* location = _session->locations()->auto_loop_location();
1598         list<Selectable *> touched;
1599
1600         if (location == 0 || (location->end() - location->start() <= 1))  {
1601                 return;
1602         }
1603
1604
1605         TrackViewList* ts;
1606
1607         if (selection->tracks.empty()) {
1608                 ts = &track_views;
1609         } else {
1610                 ts = &selection->tracks;
1611         }
1612
1613         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1614                 if ((*iter)->hidden()) {
1615                         continue;
1616                 }
1617                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1618         }
1619         begin_reversible_command (_("select all from loop"));
1620         selection->set (touched);
1621         commit_reversible_command ();
1622
1623 }
1624
1625 void
1626 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1627 {
1628         framepos_t start;
1629         framepos_t end;
1630         list<Selectable *> touched;
1631
1632         if (after) {
1633                 start = cursor->current_frame;
1634                 end = _session->current_end_frame();
1635         } else {
1636                 if (cursor->current_frame > 0) {
1637                         start = 0;
1638                         end = cursor->current_frame - 1;
1639                 } else {
1640                         return;
1641                 }
1642         }
1643
1644         if (_internal_editing) {
1645                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1646                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1647                         if (mrv) {
1648                                 mrv->select_range (start, end);
1649                         }
1650                 }
1651                 return;
1652         }
1653
1654         if (after) {
1655                 begin_reversible_command (_("select all after cursor"));
1656         } else {
1657                 begin_reversible_command (_("select all before cursor"));
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_using_edit (bool after)
1680 {
1681         framepos_t start;
1682         framepos_t end;
1683         list<Selectable *> touched;
1684
1685         if (after) {
1686                 start = get_preferred_edit_position();
1687                 end = _session->current_end_frame();
1688         } else {
1689                 if ((end = get_preferred_edit_position()) > 1) {
1690                         start = 0;
1691                         end -= 1;
1692                 } else {
1693                         return;
1694                 }
1695         }
1696
1697         if (_internal_editing) {
1698                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1699                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1700                         mrv->select_range (start, end);
1701                 }
1702                 return;
1703         }
1704
1705         if (after) {
1706                 begin_reversible_command (_("select all after edit"));
1707         } else {
1708                 begin_reversible_command (_("select all before edit"));
1709         }
1710
1711         TrackViewList* ts;
1712
1713         if (selection->tracks.empty()) {
1714                 ts = &track_views;
1715         } else {
1716                 ts = &selection->tracks;
1717         }
1718
1719         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1720                 if ((*iter)->hidden()) {
1721                         continue;
1722                 }
1723                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1724         }
1725         selection->set (touched);
1726         commit_reversible_command ();
1727 }
1728
1729 void
1730 Editor::select_all_selectables_between (bool /*within*/)
1731 {
1732         framepos_t start;
1733         framepos_t end;
1734         list<Selectable *> touched;
1735
1736         if (!get_edit_op_range (start, end)) {
1737                 return;
1738         }
1739
1740         if (_internal_editing) {
1741                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1742                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1743                         mrv->select_range (start, end);
1744                 }
1745                 return;
1746         }
1747
1748         TrackViewList* ts;
1749
1750         if (selection->tracks.empty()) {
1751                 ts = &track_views;
1752         } else {
1753                 ts = &selection->tracks;
1754         }
1755
1756         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1757                 if ((*iter)->hidden()) {
1758                         continue;
1759                 }
1760                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1761         }
1762
1763         selection->set (touched);
1764 }
1765
1766 void
1767 Editor::select_range_between ()
1768 {
1769         framepos_t start;
1770         framepos_t end;
1771
1772         if (mouse_mode == MouseRange && !selection->time.empty()) {
1773                 selection->clear_time ();
1774         }
1775
1776         if (!get_edit_op_range (start, end)) {
1777                 return;
1778         }
1779
1780         set_mouse_mode (MouseRange);
1781         selection->set (start, end);
1782 }
1783
1784 bool
1785 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1786 {
1787         framepos_t m;
1788         bool ignored;
1789
1790         /* in range mode, use any existing selection */
1791
1792         if (mouse_mode == MouseRange && !selection->time.empty()) {
1793                 /* we know that these are ordered */
1794                 start = selection->time.start();
1795                 end = selection->time.end_frame();
1796                 return true;
1797         }
1798
1799         if (!mouse_frame (m, ignored)) {
1800                 /* mouse is not in a canvas, try playhead+selected marker.
1801                    this is probably most true when using menus.
1802                 */
1803
1804                 if (selection->markers.empty()) {
1805                         return false;
1806                 }
1807
1808                 start = selection->markers.front()->position();
1809                 end = _session->audible_frame();
1810
1811         } else {
1812
1813                 switch (_edit_point) {
1814                 case EditAtPlayhead:
1815                         if (selection->markers.empty()) {
1816                                 /* use mouse + playhead */
1817                                 start = m;
1818                                 end = _session->audible_frame();
1819                         } else {
1820                                 /* use playhead + selected marker */
1821                                 start = _session->audible_frame();
1822                                 end = selection->markers.front()->position();
1823                         }
1824                         break;
1825
1826                 case EditAtMouse:
1827                         /* use mouse + selected marker */
1828                         if (selection->markers.empty()) {
1829                                 start = m;
1830                                 end = _session->audible_frame();
1831                         } else {
1832                                 start = selection->markers.front()->position();
1833                                 end = m;
1834                         }
1835                         break;
1836
1837                 case EditAtSelectedMarker:
1838                         /* use mouse + selected marker */
1839                         if (selection->markers.empty()) {
1840
1841                                 MessageDialog win (_("No edit range defined"),
1842                                                    false,
1843                                                    MESSAGE_INFO,
1844                                                    BUTTONS_OK);
1845
1846                                 win.set_secondary_text (
1847                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1848
1849
1850                                 win.set_default_response (RESPONSE_CLOSE);
1851                                 win.set_position (Gtk::WIN_POS_MOUSE);
1852                                 win.show_all();
1853
1854                                 win.run ();
1855
1856                                 return false; // NO RANGE
1857                         }
1858                         start = selection->markers.front()->position();
1859                         end = m;
1860                         break;
1861                 }
1862         }
1863
1864         if (start == end) {
1865                 return false;
1866         }
1867
1868         if (start > end) {
1869                 swap (start, end);
1870         }
1871
1872         /* turn range into one delimited by start...end,
1873            not start...end-1
1874         */
1875
1876         end++;
1877
1878         return true;
1879 }
1880
1881 void
1882 Editor::deselect_all ()
1883 {
1884         selection->clear ();
1885 }
1886
1887 long
1888 Editor::select_range (framepos_t s, framepos_t e)
1889 {
1890         selection->add (clicked_axisview);
1891         selection->time.clear ();
1892         return selection->set (s, e);
1893 }