29d7a8a6bb358f806ce5fdaee40892c155703678
[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);
580                 break;
581         case Selection::Extend:
582                 selection->add (all_equivalent_regions);
583                 break;
584         case Selection::Add:
585                 selection->add (all_equivalent_regions);
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         zoomed_to_region = false;
680 }
681
682 void
683 Editor::point_selection_changed ()
684 {
685         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
686                 (*i)->set_selected_points (selection->points);
687         }
688 }
689
690 void
691 Editor::select_all_in_track (Selection::Operation op)
692 {
693         list<Selectable *> touched;
694
695         if (!clicked_trackview) {
696                 return;
697         }
698         
699         clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
700
701         switch (op) {
702         case Selection::Toggle:
703                 selection->add (touched);
704                 break;
705         case Selection::Set:
706                 selection->set (touched);
707                 break;
708         case Selection::Extend:
709                 /* meaningless, because we're selecting everything */
710                 break;
711         case Selection::Add:
712                 selection->add (touched);
713                 break;
714         }
715 }
716
717 void
718 Editor::select_all (Selection::Operation op)
719 {
720         list<Selectable *> touched;
721         
722         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
723                 if ((*iter)->hidden()) {
724                         continue;
725                 }
726                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
727         }
728         begin_reversible_command (_("select all"));
729         switch (op) {
730         case Selection::Add:
731                 selection->add (touched);
732                 break;
733         case Selection::Toggle:
734                 selection->add (touched);
735                 break;
736         case Selection::Set:
737                 selection->set (touched);
738                 break;
739         case Selection::Extend:
740                 /* meaningless, because we're selecting everything */
741                 break;
742         }
743         commit_reversible_command ();
744 }
745
746 void
747 Editor::invert_selection_in_track ()
748 {
749         list<Selectable *> touched;
750
751         if (!clicked_trackview) {
752                 return;
753         }
754         
755         clicked_trackview->get_inverted_selectables (*selection, touched);
756         selection->set (touched);
757 }
758
759 void
760 Editor::invert_selection ()
761 {
762         list<Selectable *> touched;
763         
764         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
765                 if ((*iter)->hidden()) {
766                         continue;
767                 }
768                 (*iter)->get_inverted_selectables (*selection, touched);
769         }
770
771         selection->set (touched);
772 }
773
774 bool
775 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
776 {
777         list<Selectable*> touched;
778         list<Selectable*>::size_type n = 0;
779         TrackViewList touched_tracks;
780
781         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
782                 if ((*iter)->hidden()) {
783                         continue;
784                 }
785
786                 n = touched.size();
787
788                 (*iter)->get_selectables (start, end, top, bot, touched);
789                 
790                 if (n != touched.size()) {
791                         touched_tracks.push_back (*iter);
792                 }
793         }
794
795         if (touched.empty()) {
796                 return false;
797         }
798
799         if (!touched_tracks.empty()) {
800
801                 switch (op) {
802                 case Selection::Add:
803                         selection->add (touched_tracks);
804                         break;
805                 case Selection::Toggle:
806                         selection->toggle (touched_tracks);
807                         break;
808                 case Selection::Set:
809                         selection->set (touched_tracks);
810                         break;
811                 case Selection::Extend:
812                         /* not defined yet */
813                         break;
814                 }
815         }
816
817         begin_reversible_command (_("select all within"));
818         switch (op) {
819         case Selection::Add:
820                 selection->add (touched);
821                 break;
822         case Selection::Toggle:
823                 selection->toggle (touched);
824                 break;
825         case Selection::Set:
826                 selection->set (touched);
827                 break;
828         case Selection::Extend:
829                 /* not defined yet */
830                 break;
831         }
832         
833         commit_reversible_command ();
834
835         return !touched.empty();
836 }
837
838 void
839 Editor::set_selection_from_audio_region ()
840 {
841         if (selection->regions.empty()) {
842                 return;
843         }
844
845         RegionView* rv = *(selection->regions.begin());
846         boost::shared_ptr<Region> region = rv->region();
847         
848         begin_reversible_command (_("set selection from region"));
849         selection->set (0, region->position(), region->last_frame());
850         commit_reversible_command ();
851
852         set_mouse_mode (Editing::MouseRange, false);
853 }
854
855 void
856 Editor::set_selection_from_punch()
857 {
858         Location* location;
859
860         if ((location = session->locations()->auto_punch_location()) == 0)  {
861                 return;
862         }
863
864         set_selection_from_range (*location);
865 }
866
867 void
868 Editor::set_selection_from_loop()
869 {
870         Location* location;
871
872         if ((location = session->locations()->auto_loop_location()) == 0)  {
873                 return;
874         }
875         set_selection_from_range (*location);
876 }
877
878 void
879 Editor::set_selection_from_range (Location& loc)
880 {
881         begin_reversible_command (_("set selection from range"));
882         selection->set (0, loc.start(), loc.end());
883         commit_reversible_command ();
884
885         set_mouse_mode (Editing::MouseRange, false);
886 }
887
888 void
889 Editor::select_all_selectables_using_time_selection ()
890 {
891         list<Selectable *> touched;
892
893         if (selection->time.empty()) {
894                 return;
895         }
896
897         nframes_t start = selection->time[clicked_selection].start;
898         nframes_t end = selection->time[clicked_selection].end;
899
900         if (end - start < 1)  {
901                 return;
902         }
903
904         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
905                 if ((*iter)->hidden()) {
906                         continue;
907                 }
908                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
909         }
910
911         begin_reversible_command (_("select all from range"));
912         selection->set (touched);
913         commit_reversible_command ();
914 }
915
916
917 void
918 Editor::select_all_selectables_using_punch()
919 {
920         Location* location = session->locations()->auto_punch_location();
921         list<Selectable *> touched;
922
923         if (location == 0 || (location->end() - location->start() <= 1))  {
924                 return;
925         }
926
927         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
928                 if ((*iter)->hidden()) {
929                         continue;
930                 }
931                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
932         }
933         begin_reversible_command (_("select all from punch"));
934         selection->set (touched);
935         commit_reversible_command ();
936
937 }
938
939 void
940 Editor::select_all_selectables_using_loop()
941 {
942         Location* location = session->locations()->auto_loop_location();
943         list<Selectable *> touched;
944
945         if (location == 0 || (location->end() - location->start() <= 1))  {
946                 return;
947         }
948
949         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
950                 if ((*iter)->hidden()) {
951                         continue;
952                 }
953                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
954         }
955         begin_reversible_command (_("select all from loop"));
956         selection->set (touched);
957         commit_reversible_command ();
958
959 }
960
961 void
962 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
963 {
964         nframes_t start;
965         nframes_t end;
966         list<Selectable *> touched;
967
968         if (after) {
969                 begin_reversible_command (_("select all after cursor"));
970                 start = cursor->current_frame ;
971                 end = session->current_end_frame();
972         } else {
973                 if (cursor->current_frame > 0) {
974                         begin_reversible_command (_("select all before cursor"));
975                         start = 0;
976                         end = cursor->current_frame - 1;
977                 } else {
978                         return;
979                 }
980         }
981
982         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
983                 if ((*iter)->hidden()) {
984                         continue;
985                 }
986                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
987         }
988         selection->set (touched);
989         commit_reversible_command ();
990 }
991
992 void
993 Editor::select_all_selectables_using_edit (bool after)
994 {
995         nframes_t start;
996         nframes_t end;
997         list<Selectable *> touched;
998
999         if (after) {
1000                 begin_reversible_command (_("select all after edit"));
1001                 start = get_preferred_edit_position();
1002                 end = session->current_end_frame();
1003         } else {
1004                 if ((end = get_preferred_edit_position()) > 1) {
1005                         begin_reversible_command (_("select all before edit"));
1006                         start = 0;
1007                         end -= 1;
1008                 } else {
1009                         return;
1010                 }
1011         }
1012
1013         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1014                 if ((*iter)->hidden()) {
1015                         continue;
1016                 }
1017                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1018         }
1019         selection->set (touched);
1020         commit_reversible_command ();
1021 }
1022
1023 void
1024 Editor::select_all_selectables_between (bool within)
1025 {
1026         nframes64_t start;
1027         nframes64_t end;
1028         list<Selectable *> touched;
1029
1030         if (!get_edit_op_range (start, end)) {
1031                 return;
1032         }
1033         
1034         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1035                 if ((*iter)->hidden()) {
1036                         continue;
1037                 }
1038                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1039         }
1040
1041         selection->set (touched);
1042 }
1043
1044 void
1045 Editor::select_range_between ()
1046 {
1047         nframes64_t start;
1048         nframes64_t end;
1049         
1050         if (!get_edit_op_range (start, end)) {
1051                 return;
1052         }
1053
1054         set_mouse_mode (MouseRange);
1055         selection->set ((TimeAxisView*) 0, start, end);
1056 }
1057
1058 bool
1059 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1060 {
1061         nframes64_t m;
1062         bool ignored;
1063
1064         /* in range mode, use any existing selection */
1065
1066         if (mouse_mode == MouseRange && !selection->time.empty()) {
1067                 /* we know that these are ordered */
1068                 start = selection->time.start();
1069                 end = selection->time.end_frame();
1070                 return true;
1071         }
1072
1073         if (!mouse_frame (m, ignored)) {
1074                 /* mouse is not in a canvas, try playhead+selected marker.
1075                    this is probably most true when using menus.
1076                  */
1077
1078                 if (selection->markers.empty()) {
1079                         return false;
1080                 }
1081
1082                 start = selection->markers.front()->position();
1083                 end = session->audible_frame();
1084
1085         } else {
1086
1087                 switch (_edit_point) {
1088                 case EditAtPlayhead:
1089                         if (selection->markers.empty()) {
1090                                 /* use mouse + playhead */
1091                                 start = m;
1092                                 end = session->audible_frame();
1093                         } else {
1094                                 /* use playhead + selected marker */
1095                                 start = session->audible_frame();
1096                                 end = selection->markers.front()->position();
1097                         }
1098                         break;
1099                         
1100                 case EditAtMouse:
1101                         /* use mouse + selected marker */
1102                         if (selection->markers.empty()) {
1103                                 start = m;
1104                                 end = session->audible_frame();
1105                         } else {
1106                                 start = selection->markers.front()->position();
1107                                 end = m;
1108                         }
1109                         break;
1110                         
1111                 case EditAtSelectedMarker:
1112                         /* use mouse + selected marker */
1113                         if (selection->markers.empty()) {
1114                                 
1115                                 MessageDialog win (_("No edit range defined"),
1116                                                    false,
1117                                                    MESSAGE_INFO,
1118                                                    BUTTONS_OK);
1119
1120                                 win.set_secondary_text (
1121                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1122                                 
1123
1124                                 win.set_default_response (RESPONSE_CLOSE);
1125                                 win.set_position (Gtk::WIN_POS_MOUSE);
1126                                 win.show_all();
1127                                 
1128                                 win.run ();
1129                                 
1130                                 return false; // NO RANGE
1131                         }
1132                         start = selection->markers.front()->position();
1133                         end = m;
1134                         break;
1135                 }
1136         }
1137
1138         if (start == end) {
1139                 return false;
1140         }
1141
1142         if (start > end) {
1143                 swap (start, end);
1144         }
1145
1146         return true;
1147 }
1148
1149 void
1150 Editor::deselect_all ()
1151 {
1152         selection->clear ();
1153 }