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