add set loop/punch from edit-range; don't select track when propagating region list...
[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 <pbd/stacktrace.h>
21
22 #include <ardour/diskstream.h>
23 #include <ardour/playlist.h>
24 #include <ardour/route_group.h>
25
26 #include "editor.h"
27 #include "actions.h"
28 #include "audio_time_axis.h"
29 #include "audio_region_view.h"
30 #include "audio_streamview.h"
31 #include "automation_line.h"
32
33 #include "i18n.h"
34
35 using namespace std;
36 using namespace sigc;
37 using namespace ARDOUR;
38 using namespace PBD;
39 using namespace Gtk;
40 using namespace Glib;
41 using namespace Gtkmm2ext;
42 using namespace Editing;
43
44 struct TrackViewByPositionSorter
45 {
46     bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
47             return a->y_position < b->y_position;
48     }
49 };
50
51 bool
52 Editor::extend_selection_to_track (TimeAxisView& view)
53 {
54         if (selection->selected (&view)) {
55                 /* already selected, do nothing */
56                 return false;
57         }
58
59         if (selection->tracks.empty()) {
60
61                 if (!selection->selected (&view)) {
62                         selection->set (&view);
63                         return true;
64                 } else {
65                         return false;
66                 }
67         } 
68
69         /* something is already selected, so figure out which range of things to add */
70         
71         TrackViewList to_be_added;
72         TrackViewList sorted = track_views;
73         TrackViewByPositionSorter cmp;
74         bool passed_clicked = false;
75         bool forwards = true;
76
77         sorted.sort (cmp);
78
79         if (!selection->selected (&view)) {
80                 to_be_added.push_back (&view);
81         }
82
83         /* figure out if we should go forward or backwards */
84
85         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
86
87                 if ((*i) == &view) {
88                         passed_clicked = true;
89                 }
90
91                 if (selection->selected (*i)) {
92                         if (passed_clicked) {
93                                 forwards = true;
94                         } else {
95                                 forwards = false;
96                         }
97                         break;
98                 }
99         }
100                         
101         passed_clicked = false;
102
103         if (forwards) {
104
105                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
106                                         
107                         if ((*i) == &view) {
108                                 passed_clicked = true;
109                                 continue;
110                         }
111                                         
112                         if (passed_clicked) {
113                                 if ((*i)->hidden()) {
114                                         continue;
115                                 }
116                                 if (selection->selected (*i)) {
117                                         break;
118                                 } else if (!(*i)->hidden()) {
119                                         to_be_added.push_back (*i);
120                                 }
121                         }
122                 }
123
124         } else {
125
126                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
127                                         
128                         if ((*r) == &view) {
129                                 passed_clicked = true;
130                                 continue;
131                         }
132                                         
133                         if (passed_clicked) {
134                                                 
135                                 if ((*r)->hidden()) {
136                                         continue;
137                                 }
138                                                 
139                                 if (selection->selected (*r)) {
140                                         break;
141                                 } else if (!(*r)->hidden()) {
142                                         to_be_added.push_back (*r);
143                                 }
144                         }
145                 }
146         }
147                         
148         if (!to_be_added.empty()) {
149                 selection->add (to_be_added);
150                 return true;
151         }
152         
153         return false;
154 }
155
156 void
157 Editor::select_all_tracks ()
158 {
159         selection->set (track_views);
160 }
161
162 void
163 Editor::set_selected_track_as_side_effect (bool force)
164 {
165         if (!clicked_trackview) {
166                 return;
167         }
168
169         if (!selection->tracks.empty()) {
170                 if (!selection->selected (clicked_trackview)) {
171                         selection->add (clicked_trackview);
172                 }
173
174         } else if (force) {
175                 selection->set (clicked_trackview);
176         }
177 }
178
179 void
180 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
181 {
182
183         switch (op) {
184         case Selection::Toggle:
185                 if (selection->selected (&view)) {
186                         if (!no_remove) {
187                                 selection->remove (&view);
188                         }
189                 } else {
190                         selection->add (&view);
191                 }
192                 break;
193
194         case Selection::Add:
195                 if (!selection->selected (&view)) {
196                         selection->add (&view);
197                 }
198                 break;
199
200         case Selection::Set:
201                 if (selection->selected (&view) && selection->tracks.size() > 1) {
202
203                         /* reset track selection if there is only 1 other track
204                            selected OR if no_remove is not set (its there to 
205                            prevent deselecting a multi-track selection
206                            when clicking on an already selected track
207                            for some reason.
208                         */
209
210                         if (selection->tracks.empty()) {
211                                 selection->set (&view);
212                         } else if (selection->tracks.size() == 1 || !no_remove) {
213                                 selection->set (&view);
214                         }
215                 }
216                 break;
217                 
218         case Selection::Extend:
219                 extend_selection_to_track (view);
220                 break;
221         }
222 }
223
224 void
225 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
226 {
227         if (!clicked_trackview) {
228                 return;
229         }
230         
231         if (!press) {
232                 return;
233         }
234
235         set_selected_track (*clicked_trackview, op, no_remove);
236 }
237
238 bool
239 Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove)
240 {
241         if (!clicked_control_point) {
242                 return false;
243         }
244
245         /* select this point and any others that it represents */
246
247         double y1, y2;
248         nframes_t x1, x2;
249
250         x1 = pixel_to_frame (clicked_control_point->get_x() - 10);
251         x2 = pixel_to_frame (clicked_control_point->get_x() + 10);
252         y1 = clicked_control_point->get_x() - 10;
253         y2 = clicked_control_point->get_y() + 10;
254
255         return select_all_within (x1, x2, y1, y2, selection->tracks, op);
256 }
257
258 void
259 Editor::get_relevant_audio_tracks (set<AudioTimeAxisView*>& relevant_tracks)
260 {
261         /* step one: get all selected tracks and all tracks in the relevant edit groups */
262
263         for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) {
264
265                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*ti);
266
267                 if (!atv) {
268                         continue;
269                 }
270
271                 RouteGroup* group = atv->route()->edit_group();
272
273                 if (group && group->is_active()) {
274                         
275                         /* active group for this track, loop over all tracks and get every member of the group */
276
277                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
278                                 
279                                 AudioTimeAxisView* tatv;
280                                 
281                                 if ((tatv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
282                                         
283                                         if (tatv->route()->edit_group() == group) {
284                                                 relevant_tracks.insert (tatv);
285                                         }
286                                 }
287                         }
288                 } else {
289                         relevant_tracks.insert (atv);
290                 }
291         }
292 }
293
294
295 /**
296  *  Call a slot for a given `basis' track and also for any track that is in the same
297  *  active edit group.
298  *  @param sl Slot to call.
299  *  @param basis Basis track.
300  */
301
302 void
303 Editor::mapover_audio_tracks (slot<void,AudioTimeAxisView&,uint32_t> sl, TimeAxisView* basis)
304 {
305         AudioTimeAxisView* audio_basis = dynamic_cast<AudioTimeAxisView*> (basis);
306         if (audio_basis == 0) {
307                 return;
308         }
309
310         /* work out the tracks that we will call the slot for; use
311            a set here as it will disallow possible duplicates of the
312            basis track */
313         set<AudioTimeAxisView*> tracks;
314
315         /* always call for the basis */
316         tracks.insert (audio_basis);
317
318         RouteGroup* group = audio_basis->route()->edit_group();
319         if (group && group->is_active()) {
320
321                 /* the basis is a member of an active edit group; find other members */
322                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
323                         AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*> (*i);
324                         if (v && v->route()->edit_group() == group) {
325                                 tracks.insert (v);
326                         }
327                 }
328         }
329
330         /* call the slots */
331         uint32_t const sz = tracks.size ();
332         for (set<AudioTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
333                 sl (**i, sz);
334         }
335 }
336
337 void
338 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t ignored, RegionView* basis, vector<RegionView*>* all_equivs)
339 {
340         boost::shared_ptr<Playlist> pl;
341         vector<boost::shared_ptr<Region> > results;
342         RegionView* marv;
343         boost::shared_ptr<Diskstream> ds;
344
345         if ((ds = tv.get_diskstream()) == 0) {
346                 /* bus */
347                 return;
348         }
349
350         if (&tv == &basis->get_time_axis_view()) {
351                 /* looking in same track as the original */
352                 return;
353         }
354
355         if ((pl = ds->playlist()) != 0) {
356                 pl->get_equivalent_regions (basis->region(), results);
357         }
358
359         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
360                 if ((marv = tv.view()->find_view (*ir)) != 0) {
361                         all_equivs->push_back (marv);
362                 }
363         }
364 }
365
366 void
367 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions)
368 {
369         mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview());
370         
371         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
372         
373         equivalent_regions.push_back (basis);
374 }
375
376 bool
377 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove)
378 {
379         vector<RegionView*> all_equivalent_regions;
380         bool commit = false;
381
382         if (!clicked_regionview || !clicked_audio_trackview) {
383                 return false;
384         }
385
386         if (press) {
387                 button_release_can_deselect = false;
388         } 
389
390         if (op == Selection::Toggle || op == Selection::Set) {
391
392
393                 switch (op) {
394                 case Selection::Toggle:
395                         
396                         if (selection->selected (clicked_regionview)) {
397                                 if (press) {
398
399                                         /* whatever was clicked was selected already; do nothing here but allow
400                                            the button release to deselect it
401                                         */
402
403                                         button_release_can_deselect = true;
404
405                                 } else {
406
407                                         if (button_release_can_deselect) {
408
409                                                 /* just remove this one region, but only on a permitted button release */
410
411                                                 selection->remove (clicked_regionview);
412                                                 commit = true;
413
414                                                 /* no more deselect action on button release till a new press
415                                                    finds an already selected object.
416                                                 */
417
418                                                 button_release_can_deselect = false;
419                                         }
420                                 } 
421
422                         } else {
423
424                                 if (press) {
425
426                                        if (selection->selected (clicked_audio_trackview)) {
427                                                get_equivalent_regions (clicked_regionview, all_equivalent_regions);
428                                        } else {
429                                                all_equivalent_regions.push_back (clicked_regionview);
430                                        }
431
432                                         /* add all the equivalent regions, but only on button press */
433                                         
434
435
436                                         if (!all_equivalent_regions.empty()) {
437                                                 commit = true;
438                                         }
439
440                                         selection->add (all_equivalent_regions);
441                                 } 
442                         }
443                         break;
444                         
445                 case Selection::Set:
446                         if (!selection->selected (clicked_regionview)) {
447
448                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions);
449                                 selection->set (all_equivalent_regions);
450                                 commit = true;
451                         } else {
452                                 /* no commit necessary: clicked on an already selected region */
453                                 goto out;
454                         }
455                         break;
456
457                 default:
458                         /* silly compiler */
459                         break;
460                 }
461
462         } else if (op == Selection::Extend) {
463
464                 list<Selectable*> results;
465                 nframes_t last_frame;
466                 nframes_t first_frame;
467
468                 /* 1. find the last selected regionview in the track that was clicked in */
469
470                 last_frame = 0;
471                 first_frame = max_frames;
472
473                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
474                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
475
476                                 if ((*x)->region()->last_frame() > last_frame) {
477                                         last_frame = (*x)->region()->last_frame();
478                                 }
479
480                                 if ((*x)->region()->first_frame() < first_frame) {
481                                         first_frame = (*x)->region()->first_frame();
482                                 }
483                         }
484                 }
485
486                 /* 2. figure out the boundaries for our search for new objects */
487
488                 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
489                 case OverlapNone:
490                         if (last_frame < clicked_regionview->region()->first_frame()) {
491                                 first_frame = last_frame;
492                                 last_frame = clicked_regionview->region()->last_frame();
493                         } else {
494                                 last_frame = first_frame;
495                                 first_frame = clicked_regionview->region()->first_frame();
496                         }
497                         break;
498
499                 case OverlapExternal:
500                         if (last_frame < clicked_regionview->region()->first_frame()) {
501                                 first_frame = last_frame;
502                                 last_frame = clicked_regionview->region()->last_frame();
503                         } else {
504                                 last_frame = first_frame;
505                                 first_frame = clicked_regionview->region()->first_frame();
506                         }
507                         break;
508
509                 case OverlapInternal:
510                         if (last_frame < clicked_regionview->region()->first_frame()) {
511                                 first_frame = last_frame;
512                                 last_frame = clicked_regionview->region()->last_frame();
513                         } else {
514                                 last_frame = first_frame;
515                                 first_frame = clicked_regionview->region()->first_frame();
516                         }
517                         break;
518
519                 case OverlapStart:
520                 case OverlapEnd:
521                         /* nothing to do except add clicked region to selection, since it
522                            overlaps with the existing selection in this track.
523                         */
524                         break;
525                 }
526
527                 /* 2. find all selectable objects (regionviews in this case) between that one and the end of the
528                       one that was clicked.
529                 */
530
531                 set<AudioTimeAxisView*> relevant_tracks;
532                 
533                 get_relevant_audio_tracks (relevant_tracks);
534
535                 for (set<AudioTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
536                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
537                 }
538                 
539                 /* 3. convert to a vector of audio regions */
540
541                 vector<RegionView*> regions;
542                 
543                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
544                         RegionView* arv;
545
546                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
547                                 regions.push_back (arv);
548                         }
549                 }
550
551                 if (!regions.empty()) {
552                         selection->add (regions);
553                         commit = true;
554                 }
555         }
556
557   out:
558         return commit;
559 }
560
561
562 void
563 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
564 {
565         vector<RegionView*> all_equivalent_regions;
566
567         get_regions_corresponding_to (region, all_equivalent_regions);
568
569         if (all_equivalent_regions.empty()) {
570                 return;
571         }
572
573         switch (op) {
574         case Selection::Toggle:
575                 /* XXX this is not correct */
576                 selection->toggle (all_equivalent_regions);
577                 break;
578         case Selection::Set:
579                 selection->set (all_equivalent_regions, false);
580                 break;
581         case Selection::Extend:
582                 selection->add (all_equivalent_regions, false);
583                 break;
584         case Selection::Add:
585                 selection->add (all_equivalent_regions, false);
586                 break;
587         }
588 }
589
590 bool
591 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
592 {
593         RegionView* rv;
594         boost::shared_ptr<Region> r (weak_r.lock());
595
596         if (!r) {
597                 return true;
598         }
599
600         boost::shared_ptr<AudioRegion> ar;
601
602         if ((ar = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
603                 return true;
604         }
605
606         if ((rv = sv->find_view (ar)) == 0) {
607                 return true;
608         }
609
610         /* don't reset the selection if its something other than 
611            a single other region.
612         */
613
614         if (selection->regions.size() > 1) {
615                 return true;
616         }
617         
618         begin_reversible_command (_("set selected regions"));
619         
620         selection->set (rv);
621
622         commit_reversible_command () ;
623
624         return true;
625 }
626
627 void
628 Editor::track_selection_changed ()
629 {
630         switch (selection->tracks.size()){
631         case 0:
632                 break;
633         default:
634                 set_selected_mixer_strip (*(selection->tracks.front()));
635                 break;
636         }
637
638         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
639                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
640                         (*i)->set_selected (true);
641                 } else {
642                         (*i)->set_selected (false);
643                 }
644         }
645 }
646
647 void
648 Editor::time_selection_changed ()
649 {
650         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
651                 (*i)->hide_selection ();
652         }
653
654         if (selection->tracks.empty()) {
655                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
656                         (*i)->show_selection (selection->time);
657                 }
658         } else {
659                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
660                         (*i)->show_selection (selection->time);
661                 }
662         }
663
664         if (selection->time.empty()) {
665                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
666         } else {
667                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
668         }
669
670 }
671
672 void
673 Editor::region_selection_changed ()
674 {
675         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
676                 (*i)->set_selected_regionviews (selection->regions);
677         }
678 }
679
680 void
681 Editor::point_selection_changed ()
682 {
683         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
684                 (*i)->set_selected_points (selection->points);
685         }
686 }
687
688 void
689 Editor::select_all_in_track (Selection::Operation op)
690 {
691         list<Selectable *> touched;
692
693         if (!clicked_trackview) {
694                 return;
695         }
696         
697         clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
698
699         switch (op) {
700         case Selection::Toggle:
701                 selection->add (touched);
702                 break;
703         case Selection::Set:
704                 selection->set (touched);
705                 break;
706         case Selection::Extend:
707                 /* meaningless, because we're selecting everything */
708                 break;
709         case Selection::Add:
710                 selection->add (touched);
711                 break;
712         }
713 }
714
715 void
716 Editor::select_all (Selection::Operation op)
717 {
718         list<Selectable *> touched;
719         
720         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
721                 if ((*iter)->hidden()) {
722                         continue;
723                 }
724                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
725         }
726         begin_reversible_command (_("select all"));
727         switch (op) {
728         case Selection::Add:
729                 selection->add (touched);
730                 break;
731         case Selection::Toggle:
732                 selection->add (touched);
733                 break;
734         case Selection::Set:
735                 selection->set (touched);
736                 break;
737         case Selection::Extend:
738                 /* meaningless, because we're selecting everything */
739                 break;
740         }
741         commit_reversible_command ();
742 }
743
744 void
745 Editor::invert_selection_in_track ()
746 {
747         list<Selectable *> touched;
748
749         if (!clicked_trackview) {
750                 return;
751         }
752         
753         clicked_trackview->get_inverted_selectables (*selection, touched);
754         selection->set (touched);
755 }
756
757 void
758 Editor::invert_selection ()
759 {
760         list<Selectable *> touched;
761         
762         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
763                 if ((*iter)->hidden()) {
764                         continue;
765                 }
766                 (*iter)->get_inverted_selectables (*selection, touched);
767         }
768
769         selection->set (touched);
770 }
771
772 bool
773 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
774 {
775         list<Selectable*> touched;
776         list<Selectable*>::size_type n = 0;
777         TrackViewList touched_tracks;
778
779         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
780                 if ((*iter)->hidden()) {
781                         continue;
782                 }
783
784                 n = touched.size();
785
786                 (*iter)->get_selectables (start, end, top, bot, touched);
787                 
788                 if (n != touched.size()) {
789                         touched_tracks.push_back (*iter);
790                 }
791         }
792
793         if (touched.empty()) {
794                 return false;
795         }
796
797         if (!touched_tracks.empty()) {
798
799                 switch (op) {
800                 case Selection::Add:
801                         selection->add (touched_tracks);
802                         break;
803                 case Selection::Toggle:
804                         selection->toggle (touched_tracks);
805                         break;
806                 case Selection::Set:
807                         selection->set (touched_tracks);
808                         break;
809                 case Selection::Extend:
810                         /* not defined yet */
811                         break;
812                 }
813         }
814
815         begin_reversible_command (_("select all within"));
816         switch (op) {
817         case Selection::Add:
818                 selection->add (touched);
819                 break;
820         case Selection::Toggle:
821                 selection->toggle (touched);
822                 break;
823         case Selection::Set:
824                 selection->set (touched);
825                 break;
826         case Selection::Extend:
827                 /* not defined yet */
828                 break;
829         }
830         
831         commit_reversible_command ();
832
833         return !touched.empty();
834 }
835
836 void
837 Editor::set_selection_from_audio_region ()
838 {
839         if (selection->regions.empty()) {
840                 return;
841         }
842
843         RegionView* rv = *(selection->regions.begin());
844         boost::shared_ptr<Region> region = rv->region();
845         
846         begin_reversible_command (_("set selection from region"));
847         selection->set (0, region->position(), region->last_frame());
848         commit_reversible_command ();
849
850         set_mouse_mode (Editing::MouseRange, false);
851 }
852
853 void
854 Editor::set_selection_from_punch()
855 {
856         Location* location;
857
858         if ((location = session->locations()->auto_punch_location()) == 0)  {
859                 return;
860         }
861
862         set_selection_from_range (*location);
863 }
864
865 void
866 Editor::set_selection_from_loop()
867 {
868         Location* location;
869
870         if ((location = session->locations()->auto_loop_location()) == 0)  {
871                 return;
872         }
873         set_selection_from_range (*location);
874 }
875
876 void
877 Editor::set_selection_from_range (Location& loc)
878 {
879         begin_reversible_command (_("set selection from range"));
880         selection->set (0, loc.start(), loc.end());
881         commit_reversible_command ();
882
883         set_mouse_mode (Editing::MouseRange, false);
884 }
885
886 void
887 Editor::select_all_selectables_using_time_selection ()
888 {
889         list<Selectable *> touched;
890
891         if (selection->time.empty()) {
892                 return;
893         }
894
895         nframes_t start = selection->time[clicked_selection].start;
896         nframes_t end = selection->time[clicked_selection].end;
897
898         if (end - start < 1)  {
899                 return;
900         }
901
902         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
903                 if ((*iter)->hidden()) {
904                         continue;
905                 }
906                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
907         }
908
909         begin_reversible_command (_("select all from range"));
910         selection->set (touched);
911         commit_reversible_command ();
912 }
913
914
915 void
916 Editor::select_all_selectables_using_punch()
917 {
918         Location* location = session->locations()->auto_punch_location();
919         list<Selectable *> touched;
920
921         if (location == 0 || (location->end() - location->start() <= 1))  {
922                 return;
923         }
924
925         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
926                 if ((*iter)->hidden()) {
927                         continue;
928                 }
929                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
930         }
931         begin_reversible_command (_("select all from punch"));
932         selection->set (touched);
933         commit_reversible_command ();
934
935 }
936
937 void
938 Editor::select_all_selectables_using_loop()
939 {
940         Location* location = session->locations()->auto_loop_location();
941         list<Selectable *> touched;
942
943         if (location == 0 || (location->end() - location->start() <= 1))  {
944                 return;
945         }
946
947         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
948                 if ((*iter)->hidden()) {
949                         continue;
950                 }
951                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
952         }
953         begin_reversible_command (_("select all from loop"));
954         selection->set (touched);
955         commit_reversible_command ();
956
957 }
958
959 void
960 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
961 {
962         nframes_t start;
963         nframes_t end;
964         list<Selectable *> touched;
965
966         if (after) {
967                 begin_reversible_command (_("select all after cursor"));
968                 start = cursor->current_frame ;
969                 end = session->current_end_frame();
970         } else {
971                 if (cursor->current_frame > 0) {
972                         begin_reversible_command (_("select all before cursor"));
973                         start = 0;
974                         end = cursor->current_frame - 1;
975                 } else {
976                         return;
977                 }
978         }
979
980         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
981                 if ((*iter)->hidden()) {
982                         continue;
983                 }
984                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
985         }
986         selection->set (touched);
987         commit_reversible_command ();
988 }
989
990 void
991 Editor::select_all_selectables_using_edit (bool after)
992 {
993         nframes_t start;
994         nframes_t end;
995         list<Selectable *> touched;
996
997         if (after) {
998                 begin_reversible_command (_("select all after edit"));
999                 start = get_preferred_edit_position();
1000                 end = session->current_end_frame();
1001         } else {
1002                 if ((end = get_preferred_edit_position()) > 1) {
1003                         begin_reversible_command (_("select all before edit"));
1004                         start = 0;
1005                         end -= 1;
1006                 } else {
1007                         return;
1008                 }
1009         }
1010
1011         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1012                 if ((*iter)->hidden()) {
1013                         continue;
1014                 }
1015                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1016         }
1017         selection->set (touched);
1018         commit_reversible_command ();
1019 }
1020
1021 void
1022 Editor::select_all_selectables_between (bool within)
1023 {
1024         nframes64_t start;
1025         nframes64_t end;
1026         list<Selectable *> touched;
1027
1028         if (!get_edit_op_range (start, end)) {
1029                 return;
1030         }
1031         
1032         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1033                 if ((*iter)->hidden()) {
1034                         continue;
1035                 }
1036                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1037         }
1038
1039         selection->set (touched);
1040 }
1041
1042 void
1043 Editor::select_range_between ()
1044 {
1045         nframes64_t start;
1046         nframes64_t end;
1047         
1048         if (!get_edit_op_range (start, end)) {
1049                 return;
1050         }
1051
1052         set_mouse_mode (MouseRange);
1053         selection->set ((TimeAxisView*) 0, start, end);
1054 }
1055
1056 bool
1057 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1058 {
1059         nframes64_t m;
1060         bool ignored;
1061
1062         /* in range mode, use any existing selection */
1063
1064         if (mouse_mode == MouseRange && !selection->time.empty()) {
1065                 /* we know that these are ordered */
1066                 start = selection->time.start();
1067                 end = selection->time.end_frame();
1068                 return true;
1069         }
1070
1071         if (!mouse_frame (m, ignored)) {
1072                 /* mouse is not in a canvas, try playhead+selected marker.
1073                    this is probably most true when using menus.
1074                  */
1075
1076                 if (selection->markers.empty()) {
1077                         return false;
1078                 }
1079
1080                 start = selection->markers.front()->position();
1081                 end = session->audible_frame();
1082
1083         } else {
1084
1085                 switch (_edit_point) {
1086                 case EditAtPlayhead:
1087                         if (selection->markers.empty()) {
1088                                 /* use mouse + playhead */
1089                                 start = m;
1090                                 end = session->audible_frame();
1091                         } else {
1092                                 /* use playhead + selected marker */
1093                                 start = session->audible_frame();
1094                                 end = selection->markers.front()->position();
1095                         }
1096                         break;
1097                         
1098                 case EditAtMouse:
1099                 case EditAtSelectedMarker:
1100                         /* use mouse + selected marker */
1101                         if (selection->markers.empty()) {
1102                                 return false;
1103                         }
1104                         start = selection->markers.front()->position();
1105                         end = m;
1106                         break;
1107                 }
1108         }
1109
1110         if (start == end) {
1111                 return false;
1112         }
1113
1114         if (start > end) {
1115                 swap (start, end);
1116         }
1117
1118         return true;
1119 }