proper fix for stereo region wave drawing bug; make trackheight operations apply...
[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         RegionView* rv = *(selection->regions.begin());
967         boost::shared_ptr<Region> region = rv->region();
968         
969         begin_reversible_command (_("set selection from region"));
970         selection->set (0, region->position(), region->last_frame());
971         commit_reversible_command ();
972
973         set_mouse_mode (Editing::MouseRange, false);
974 }
975
976 void
977 Editor::set_selection_from_punch()
978 {
979         Location* location;
980
981         if ((location = session->locations()->auto_punch_location()) == 0)  {
982                 return;
983         }
984
985         set_selection_from_range (*location);
986 }
987
988 void
989 Editor::set_selection_from_loop()
990 {
991         Location* location;
992
993         if ((location = session->locations()->auto_loop_location()) == 0)  {
994                 return;
995         }
996         set_selection_from_range (*location);
997 }
998
999 void
1000 Editor::set_selection_from_range (Location& loc)
1001 {
1002         begin_reversible_command (_("set selection from range"));
1003         selection->set (0, loc.start(), loc.end());
1004         commit_reversible_command ();
1005
1006         set_mouse_mode (Editing::MouseRange, false);
1007 }
1008
1009 void
1010 Editor::select_all_selectables_using_time_selection ()
1011 {
1012         list<Selectable *> touched;
1013
1014         if (selection->time.empty()) {
1015                 return;
1016         }
1017
1018         nframes_t start = selection->time[clicked_selection].start;
1019         nframes_t end = selection->time[clicked_selection].end;
1020
1021         if (end - start < 1)  {
1022                 return;
1023         }
1024
1025         TrackSelection* ts;
1026
1027         if (selection->tracks.empty()) {
1028                 ts = &track_views;
1029         } else {
1030                 ts = &selection->tracks;
1031         }
1032
1033         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1034                 if ((*iter)->hidden()) {
1035                         continue;
1036                 }
1037                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1038         }
1039
1040         begin_reversible_command (_("select all from range"));
1041         selection->set (touched);
1042         commit_reversible_command ();
1043 }
1044
1045
1046 void
1047 Editor::select_all_selectables_using_punch()
1048 {
1049         Location* location = session->locations()->auto_punch_location();
1050         list<Selectable *> touched;
1051
1052         if (location == 0 || (location->end() - location->start() <= 1))  {
1053                 return;
1054         }
1055
1056
1057         TrackSelection* ts;
1058
1059         if (selection->tracks.empty()) {
1060                 ts = &track_views;
1061         } else {
1062                 ts = &selection->tracks;
1063         }
1064
1065         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1066                 if ((*iter)->hidden()) {
1067                         continue;
1068                 }
1069                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1070         }
1071         begin_reversible_command (_("select all from punch"));
1072         selection->set (touched);
1073         commit_reversible_command ();
1074
1075 }
1076
1077 void
1078 Editor::select_all_selectables_using_loop()
1079 {
1080         Location* location = session->locations()->auto_loop_location();
1081         list<Selectable *> touched;
1082
1083         if (location == 0 || (location->end() - location->start() <= 1))  {
1084                 return;
1085         }
1086
1087
1088         TrackSelection* ts;
1089
1090         if (selection->tracks.empty()) {
1091                 ts = &track_views;
1092         } else {
1093                 ts = &selection->tracks;
1094         }
1095
1096         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1097                 if ((*iter)->hidden()) {
1098                         continue;
1099                 }
1100                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1101         }
1102         begin_reversible_command (_("select all from loop"));
1103         selection->set (touched);
1104         commit_reversible_command ();
1105
1106 }
1107
1108 void
1109 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1110 {
1111         nframes_t start;
1112         nframes_t end;
1113         list<Selectable *> touched;
1114
1115         if (after) {
1116                 begin_reversible_command (_("select all after cursor"));
1117                 start = cursor->current_frame ;
1118                 end = session->current_end_frame();
1119         } else {
1120                 if (cursor->current_frame > 0) {
1121                         begin_reversible_command (_("select all before cursor"));
1122                         start = 0;
1123                         end = cursor->current_frame - 1;
1124                 } else {
1125                         return;
1126                 }
1127         }
1128
1129
1130         TrackSelection* ts;
1131
1132         if (selection->tracks.empty()) {
1133                 ts = &track_views;
1134         } else {
1135                 ts = &selection->tracks;
1136         }
1137
1138         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1139                 if ((*iter)->hidden()) {
1140                         continue;
1141                 }
1142                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1143         }
1144         selection->set (touched);
1145         commit_reversible_command ();
1146 }
1147
1148 void
1149 Editor::select_all_selectables_using_edit (bool after)
1150 {
1151         nframes_t start;
1152         nframes_t end;
1153         list<Selectable *> touched;
1154
1155         if (after) {
1156                 begin_reversible_command (_("select all after edit"));
1157                 start = get_preferred_edit_position();
1158                 end = session->current_end_frame();
1159         } else {
1160                 if ((end = get_preferred_edit_position()) > 1) {
1161                         begin_reversible_command (_("select all before edit"));
1162                         start = 0;
1163                         end -= 1;
1164                 } else {
1165                         return;
1166                 }
1167         }
1168
1169
1170         TrackSelection* ts;
1171
1172         if (selection->tracks.empty()) {
1173                 ts = &track_views;
1174         } else {
1175                 ts = &selection->tracks;
1176         }
1177
1178         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1179                 if ((*iter)->hidden()) {
1180                         continue;
1181                 }
1182                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1183         }
1184         selection->set (touched);
1185         commit_reversible_command ();
1186 }
1187
1188 void
1189 Editor::select_all_selectables_between (bool within)
1190 {
1191         nframes64_t start;
1192         nframes64_t end;
1193         list<Selectable *> touched;
1194
1195         if (!get_edit_op_range (start, end)) {
1196                 return;
1197         }
1198
1199         TrackSelection* ts;
1200
1201         if (selection->tracks.empty()) {
1202                 ts = &track_views;
1203         } else {
1204                 ts = &selection->tracks;
1205         }
1206
1207         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1208                 if ((*iter)->hidden()) {
1209                         continue;
1210                 }
1211                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1212         }
1213
1214         selection->set (touched);
1215 }
1216
1217 void
1218 Editor::select_range_between ()
1219 {
1220         nframes64_t start;
1221         nframes64_t end;
1222         
1223         if (!get_edit_op_range (start, end)) {
1224                 return;
1225         }
1226
1227         set_mouse_mode (MouseRange);
1228         selection->set ((TimeAxisView*) 0, start, end);
1229 }
1230
1231 bool
1232 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1233 {
1234         nframes64_t m;
1235         bool ignored;
1236
1237         /* in range mode, use any existing selection */
1238
1239         if (mouse_mode == MouseRange && !selection->time.empty()) {
1240                 /* we know that these are ordered */
1241                 start = selection->time.start();
1242                 end = selection->time.end_frame();
1243                 return true;
1244         }
1245
1246         if (!mouse_frame (m, ignored)) {
1247                 /* mouse is not in a canvas, try playhead+selected marker.
1248                    this is probably most true when using menus.
1249                  */
1250
1251                 if (selection->markers.empty()) {
1252                         return false;
1253                 }
1254
1255                 start = selection->markers.front()->position();
1256                 end = session->audible_frame();
1257
1258         } else {
1259
1260                 switch (_edit_point) {
1261                 case EditAtPlayhead:
1262                         if (selection->markers.empty()) {
1263                                 /* use mouse + playhead */
1264                                 start = m;
1265                                 end = session->audible_frame();
1266                         } else {
1267                                 /* use playhead + selected marker */
1268                                 start = session->audible_frame();
1269                                 end = selection->markers.front()->position();
1270                         }
1271                         break;
1272                         
1273                 case EditAtMouse:
1274                         /* use mouse + selected marker */
1275                         if (selection->markers.empty()) {
1276                                 start = m;
1277                                 end = session->audible_frame();
1278                         } else {
1279                                 start = selection->markers.front()->position();
1280                                 end = m;
1281                         }
1282                         break;
1283                         
1284                 case EditAtSelectedMarker:
1285                         /* use mouse + selected marker */
1286                         if (selection->markers.empty()) {
1287                                 
1288                                 MessageDialog win (_("No edit range defined"),
1289                                                    false,
1290                                                    MESSAGE_INFO,
1291                                                    BUTTONS_OK);
1292
1293                                 win.set_secondary_text (
1294                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1295                                 
1296
1297                                 win.set_default_response (RESPONSE_CLOSE);
1298                                 win.set_position (Gtk::WIN_POS_MOUSE);
1299                                 win.show_all();
1300                                 
1301                                 win.run ();
1302                                 
1303                                 return false; // NO RANGE
1304                         }
1305                         start = selection->markers.front()->position();
1306                         end = m;
1307                         break;
1308                 }
1309         }
1310
1311         if (start == end) {
1312                 return false;
1313         }
1314
1315         if (start > end) {
1316                 swap (start, end);
1317         }
1318
1319         return true;
1320 }
1321
1322 void
1323 Editor::deselect_all ()
1324 {
1325         selection->clear ();
1326 }
1327
1328