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