1408351d4768f0665871bf6c3b5d0c1b87378e8e
[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 #include "control_point.h"
36
37 #include "i18n.h"
38
39 using namespace std;
40 using namespace sigc;
41 using namespace ARDOUR;
42 using namespace PBD;
43 using namespace Gtk;
44 using namespace Glib;
45 using namespace Gtkmm2ext;
46 using namespace Editing;
47
48 struct TrackViewByPositionSorter
49 {
50     bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
51             return a->y_position < b->y_position;
52     }
53 };
54
55 bool
56 Editor::extend_selection_to_track (TimeAxisView& view)
57 {
58         if (selection->selected (&view)) {
59                 /* already selected, do nothing */
60                 return false;
61         }
62
63         if (selection->tracks.empty()) {
64
65                 if (!selection->selected (&view)) {
66                         selection->set (&view);
67                         return true;
68                 } else {
69                         return false;
70                 }
71         } 
72
73         /* something is already selected, so figure out which range of things to add */
74         
75         TrackViewList to_be_added;
76         TrackViewList sorted = track_views;
77         TrackViewByPositionSorter cmp;
78         bool passed_clicked = false;
79         bool forwards = true;
80
81         sorted.sort (cmp);
82
83         if (!selection->selected (&view)) {
84                 to_be_added.push_back (&view);
85         }
86
87         /* figure out if we should go forward or backwards */
88
89         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
90
91                 if ((*i) == &view) {
92                         passed_clicked = true;
93                 }
94
95                 if (selection->selected (*i)) {
96                         if (passed_clicked) {
97                                 forwards = true;
98                         } else {
99                                 forwards = false;
100                         }
101                         break;
102                 }
103         }
104                         
105         passed_clicked = false;
106
107         if (forwards) {
108
109                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
110                                         
111                         if ((*i) == &view) {
112                                 passed_clicked = true;
113                                 continue;
114                         }
115                                         
116                         if (passed_clicked) {
117                                 if ((*i)->hidden()) {
118                                         continue;
119                                 }
120                                 if (selection->selected (*i)) {
121                                         break;
122                                 } else if (!(*i)->hidden()) {
123                                         to_be_added.push_back (*i);
124                                 }
125                         }
126                 }
127
128         } else {
129
130                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
131                                         
132                         if ((*r) == &view) {
133                                 passed_clicked = true;
134                                 continue;
135                         }
136                                         
137                         if (passed_clicked) {
138                                                 
139                                 if ((*r)->hidden()) {
140                                         continue;
141                                 }
142                                                 
143                                 if (selection->selected (*r)) {
144                                         break;
145                                 } else if (!(*r)->hidden()) {
146                                         to_be_added.push_back (*r);
147                                 }
148                         }
149                 }
150         }
151                         
152         if (!to_be_added.empty()) {
153                 selection->add (to_be_added);
154                 return true;
155         }
156         
157         return false;
158 }
159
160 void
161 Editor::select_all_tracks ()
162 {
163         selection->set (track_views);
164 }
165
166 void
167 Editor::set_selected_track_as_side_effect (bool force)
168 {
169         if (!clicked_routeview) {
170                 return;
171         }
172
173         if (!selection->tracks.empty()) {
174                 if (!selection->selected (clicked_routeview)) {
175                         selection->add (clicked_routeview);
176                 }
177
178         } else if (force) {
179                 selection->set (clicked_routeview);
180         }
181 }
182
183 void
184 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
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_routeview) {
231                 return;
232         }
233         
234         if (!press) {
235                 return;
236         }
237
238         set_selected_track (*clicked_routeview, 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_tracks (set<RouteTimeAxisView*>& 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                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*ti);
269
270                 if (!rtv) {
271                         continue;
272                 }
273
274                 RouteGroup* group = rtv->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                                 RouteTimeAxisView* trtv;
283                                 
284                                 if ((trtv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
285                                         
286                                         if (trtv->route()->edit_group() == group) {
287                                                 relevant_tracks.insert (trtv);
288                                         }
289                                 }
290                         }
291                 } else {
292                         relevant_tracks.insert (rtv);
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_tracks (slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis) const
306 {
307         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
308         if (route_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<RouteTimeAxisView*> tracks;
316
317         /* always call for the basis */
318         tracks.insert (route_basis);
319
320         RouteGroup* group = route_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::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
325                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*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<RouteTimeAxisView*>::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) const
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) const
370 {
371         mapover_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_routeview) {
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_routeview)) {
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                                 selection->set (clicked_regionview);
450                                 commit = true;
451                         } else {
452                                 /* no commit necessary: clicked on an already selected region */
453                                 goto out;
454                         }
455                         break;
456
457                 default:
458                         /* silly compiler */
459                         break;
460                 }
461
462         } else if (op == Selection::Extend) {
463
464                 list<Selectable*> results;
465                 nframes_t last_frame;
466                 nframes_t first_frame;
467                 bool same_track = false;
468
469                 /* 1. find the last selected regionview in the track that was clicked in */
470
471                 last_frame = 0;
472                 first_frame = max_frames;
473
474                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
475                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
476
477                                 if ((*x)->region()->last_frame() > last_frame) {
478                                         last_frame = (*x)->region()->last_frame();
479                                 }
480
481                                 if ((*x)->region()->first_frame() < first_frame) {
482                                         first_frame = (*x)->region()->first_frame();
483                                 }
484
485                                 same_track = true;
486                         }
487                 }
488
489                 if (same_track) {
490
491                         /* 2. figure out the boundaries for our search for new objects */
492                         
493                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
494                         case OverlapNone:
495                                 if (last_frame < clicked_regionview->region()->first_frame()) {
496                                         first_frame = last_frame;
497                                         last_frame = clicked_regionview->region()->last_frame();
498                                 } else {
499                                         last_frame = first_frame;
500                                         first_frame = clicked_regionview->region()->first_frame();
501                                 }
502                                 break;
503                                 
504                         case OverlapExternal:
505                                 if (last_frame < clicked_regionview->region()->first_frame()) {
506                                         first_frame = last_frame;
507                                         last_frame = clicked_regionview->region()->last_frame();
508                                 } else {
509                                         last_frame = first_frame;
510                                         first_frame = clicked_regionview->region()->first_frame();
511                                 }
512                                 break;
513                                 
514                         case OverlapInternal:
515                                 if (last_frame < clicked_regionview->region()->first_frame()) {
516                                         first_frame = last_frame;
517                                         last_frame = clicked_regionview->region()->last_frame();
518                                 } else {
519                                         last_frame = first_frame;
520                                         first_frame = clicked_regionview->region()->first_frame();
521                                 }
522                                 break;
523                                 
524                         case OverlapStart:
525                         case OverlapEnd:
526                                 /* nothing to do except add clicked region to selection, since it
527                                    overlaps with the existing selection in this track.
528                                 */
529                                 break;
530                         }
531
532                 } else {
533
534                         /* click in a track that has no regions selected, so extend vertically
535                            to pick out all regions that are defined by the existing selection
536                            plus this one.
537                         */
538                         
539                         
540                         first_frame = entered_regionview->region()->position();
541                         last_frame = entered_regionview->region()->last_frame();
542                         
543                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
544                                 if ((*i)->region()->position() < first_frame) {
545                                         first_frame = (*i)->region()->position();
546                                 }
547                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
548                                         last_frame = (*i)->region()->last_frame();
549                                 }
550                         }
551                 }
552
553                 /* 2. find all the tracks we should select in */
554
555                 set<RouteTimeAxisView*> relevant_tracks;
556                 set<RouteTimeAxisView*> already_in_selection;
557
558                 get_relevant_tracks (relevant_tracks);
559
560                 if (relevant_tracks.empty()) {
561
562                         /* no relevant tracks -> no tracks selected .. thus .. if
563                            the regionview we're in isn't selected (i.e. we're
564                            about to extend to it), then find all tracks between
565                            the this one and any selected ones.
566                         */
567
568                         if (!selection->selected (entered_regionview)) {
569
570                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
571
572                                 if (rtv) {
573
574                                         /* add this track to the ones we will search */
575
576                                         relevant_tracks.insert (rtv);
577
578                                         /* find the track closest to this one that
579                                            already a selected region.
580                                         */
581
582                                         RouteTimeAxisView* closest = 0;
583                                         int distance = INT_MAX;
584                                         int key = rtv->route()->order_key ("editor");
585
586                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
587
588                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
589
590                                                 if (artv && artv != rtv) {
591
592                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
593
594                                                         result = already_in_selection.insert (artv);
595
596                                                         if (result.second) {
597                                                                 /* newly added to already_in_selection */
598                                                         
599
600                                                                 int d = artv->route()->order_key ("editor");
601                                                                 
602                                                                 d -= key;
603                                                                 
604                                                                 if (abs (d) < distance) {
605                                                                         distance = abs (d);
606                                                                         closest = artv;
607                                                                 }
608                                                         }
609                                                 }
610                                         }
611                                         
612                                         if (closest) {
613
614                                                 /* now add all tracks between that one and this one */
615                                                 
616                                                 int okey = closest->route()->order_key ("editor");
617                                                 
618                                                 if (okey > key) {
619                                                         swap (okey, key);
620                                                 }
621                                                 
622                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
623                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
624                                                         if (artv && artv != rtv) {
625
626                                                                 int k = artv->route()->order_key ("editor");
627
628                                                                 if (k >= okey && k <= key) {
629
630                                                                         /* in range but don't add it if
631                                                                            it already has tracks selected.
632                                                                            this avoids odd selection
633                                                                            behaviour that feels wrong.
634                                                                         */
635
636                                                                         if (find (already_in_selection.begin(),
637                                                                                   already_in_selection.end(),
638                                                                                   artv) == already_in_selection.end()) {
639
640                                                                                 relevant_tracks.insert (artv);
641                                                                         }
642                                                                 }
643                                                         }
644                                                 }
645                                         }
646                                 }
647                         }
648                 }
649
650                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
651                            one that was clicked.
652                 */
653
654                 get_relevant_tracks (relevant_tracks);
655
656                 for (set<RouteTimeAxisView*>::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 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         begin_reversible_command (_("set selected regions"));
695         
696         switch (op) {
697         case Selection::Toggle:
698                 /* XXX this is not correct */
699                 selection->toggle (all_equivalent_regions);
700                 break;
701         case Selection::Set:
702                 selection->set (all_equivalent_regions);
703                 break;
704         case Selection::Extend:
705                 selection->add (all_equivalent_regions);
706                 break;
707         case Selection::Add:
708                 selection->add (all_equivalent_regions);
709                 break;
710         }
711
712         commit_reversible_command () ;
713 }
714
715 bool
716 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
717 {
718         RegionView* rv;
719         boost::shared_ptr<Region> r (weak_r.lock());
720
721         if (!r) {
722                 return true;
723         }
724
725         if ((rv = sv->find_view (r)) == 0) {
726                 return true;
727         }
728
729         /* don't reset the selection if its something other than 
730            a single other region.
731         */
732
733         if (selection->regions.size() > 1) {
734                 return true;
735         }
736         
737         begin_reversible_command (_("set selected regions"));
738         
739         selection->set (rv);
740
741         commit_reversible_command () ;
742
743         return true;
744 }
745
746 void
747 Editor::track_selection_changed ()
748 {
749         switch (selection->tracks.size()){
750         case 0:
751                 break;
752         default:
753                 set_selected_mixer_strip (*(selection->tracks.front()));
754                 break;
755         }
756
757         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
758                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
759                         (*i)->set_selected (true);
760                 } else {
761                         (*i)->set_selected (false);
762                 }
763         }
764 }
765
766 void
767 Editor::time_selection_changed ()
768 {
769         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
770                 (*i)->hide_selection ();
771         }
772
773         if (selection->tracks.empty()) {
774                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
775                         (*i)->show_selection (selection->time);
776                 }
777         } else {
778                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
779                         (*i)->show_selection (selection->time);
780                 }
781         }
782
783         if (selection->time.empty()) {
784                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
785         } else {
786                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
787         }
788
789 }
790
791 void
792 Editor::region_selection_changed ()
793 {
794         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
795                 (*i)->set_selected_regionviews (selection->regions);
796         }
797         
798         bool have_selected_regions = !selection->regions.empty();
799
800         for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
801              x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
802
803                 string accel_path = (*x)->get_accel_path ();
804                 AccelKey key;
805
806                 /* if there is an accelerator, it should always be sensitive
807                    to allow for keyboard ops on entered regions.
808                 */
809
810                 bool known = ActionManager::lookup_entry (accel_path, key);
811
812                 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
813                         (*x)->set_sensitive (true);
814                 } else {
815                         (*x)->set_sensitive (have_selected_regions);
816                 }
817         }
818
819         zoomed_to_region = false;
820 }
821
822 void
823 Editor::point_selection_changed ()
824 {
825         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
826                 (*i)->set_selected_points (selection->points);
827         }
828 }
829
830 void
831 Editor::select_all_in_track (Selection::Operation op)
832 {
833         list<Selectable *> touched;
834
835         if (!clicked_routeview) {
836                 return;
837         }
838         
839         clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
840
841         switch (op) {
842         case Selection::Toggle:
843                 selection->add (touched);
844                 break;
845         case Selection::Set:
846                 selection->set (touched);
847                 break;
848         case Selection::Extend:
849                 /* meaningless, because we're selecting everything */
850                 break;
851         case Selection::Add:
852                 selection->add (touched);
853                 break;
854         }
855 }
856
857 void
858 Editor::select_all (Selection::Operation op)
859 {
860         list<Selectable *> touched;
861
862         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
863                 if ((*iter)->hidden()) {
864                         continue;
865                 }
866                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
867         }
868         begin_reversible_command (_("select all"));
869         switch (op) {
870         case Selection::Add:
871                 selection->add (touched);
872                 break;
873         case Selection::Toggle:
874                 selection->add (touched);
875                 break;
876         case Selection::Set:
877                 selection->set (touched);
878                 break;
879         case Selection::Extend:
880                 /* meaningless, because we're selecting everything */
881                 break;
882         }
883         commit_reversible_command ();
884 }
885 void
886 Editor::invert_selection_in_track ()
887 {
888         list<Selectable *> touched;
889
890         if (!clicked_routeview) {
891                 return;
892         }
893         
894         clicked_routeview->get_inverted_selectables (*selection, touched);
895         selection->set (touched);
896 }
897
898 void
899 Editor::invert_selection ()
900 {
901         list<Selectable *> touched;
902         
903         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
904                 if ((*iter)->hidden()) {
905                         continue;
906                 }
907                 (*iter)->get_inverted_selectables (*selection, touched);
908         }
909
910         selection->set (touched);
911 }
912
913 bool
914 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
915 {
916         list<Selectable*> touched;
917         list<Selectable*>::size_type n = 0;
918         TrackViewList touched_tracks;
919
920         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
921                 if ((*iter)->hidden()) {
922                         continue;
923                 }
924
925                 n = touched.size();
926
927                 (*iter)->get_selectables (start, end, top, bot, touched);
928                 
929                 if (n != touched.size()) {
930                         touched_tracks.push_back (*iter);
931                 }
932         }
933
934         if (touched.empty()) {
935                 return false;
936         }
937
938         if (!touched_tracks.empty()) {
939
940                 switch (op) {
941                 case Selection::Add:
942                         selection->add (touched_tracks);
943                         break;
944                 case Selection::Toggle:
945                         selection->toggle (touched_tracks);
946                         break;
947                 case Selection::Set:
948                         selection->set (touched_tracks);
949                         break;
950                 case Selection::Extend:
951                         /* not defined yet */
952                         break;
953                 }
954         }
955
956         begin_reversible_command (_("select all within"));
957         switch (op) {
958         case Selection::Add:
959                 selection->add (touched);
960                 break;
961         case Selection::Toggle:
962                 selection->toggle (touched);
963                 break;
964         case Selection::Set:
965                 selection->set (touched);
966                 break;
967         case Selection::Extend:
968                 /* not defined yet */
969                 break;
970         }
971         
972         commit_reversible_command ();
973
974         return !touched.empty();
975 }
976
977 void
978 Editor::set_selection_from_region ()
979 {
980         if (selection->regions.empty()) {
981                 return;
982         }
983
984         selection->set (0, selection->regions.start(), selection->regions.end_frame());
985         set_mouse_mode (Editing::MouseRange, false);
986 }
987
988 void
989 Editor::set_selection_from_punch()
990 {
991         Location* location;
992
993         if ((location = session->locations()->auto_punch_location()) == 0)  {
994                 return;
995         }
996
997         set_selection_from_range (*location);
998 }
999
1000 void
1001 Editor::set_selection_from_loop()
1002 {
1003         Location* location;
1004
1005         if ((location = session->locations()->auto_loop_location()) == 0)  {
1006                 return;
1007         }
1008         set_selection_from_range (*location);
1009 }
1010
1011 void
1012 Editor::set_selection_from_range (Location& loc)
1013 {
1014         begin_reversible_command (_("set selection from range"));
1015         selection->set (0, loc.start(), loc.end());
1016         commit_reversible_command ();
1017
1018         set_mouse_mode (Editing::MouseRange, false);
1019 }
1020
1021 void
1022 Editor::select_all_selectables_using_time_selection ()
1023 {
1024         list<Selectable *> touched;
1025
1026         if (selection->time.empty()) {
1027                 return;
1028         }
1029
1030         nframes_t start = selection->time[clicked_selection].start;
1031         nframes_t end = selection->time[clicked_selection].end;
1032
1033         if (end - start < 1)  {
1034                 return;
1035         }
1036
1037         TrackSelection* ts;
1038
1039         if (selection->tracks.empty()) {
1040                 ts = &track_views;
1041         } else {
1042                 ts = &selection->tracks;
1043         }
1044
1045         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1046                 if ((*iter)->hidden()) {
1047                         continue;
1048                 }
1049                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1050         }
1051
1052         begin_reversible_command (_("select all from range"));
1053         selection->set (touched);
1054         commit_reversible_command ();
1055 }
1056
1057
1058 void
1059 Editor::select_all_selectables_using_punch()
1060 {
1061         Location* location = session->locations()->auto_punch_location();
1062         list<Selectable *> touched;
1063
1064         if (location == 0 || (location->end() - location->start() <= 1))  {
1065                 return;
1066         }
1067
1068
1069         TrackSelection* ts;
1070
1071         if (selection->tracks.empty()) {
1072                 ts = &track_views;
1073         } else {
1074                 ts = &selection->tracks;
1075         }
1076
1077         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1078                 if ((*iter)->hidden()) {
1079                         continue;
1080                 }
1081                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1082         }
1083         begin_reversible_command (_("select all from punch"));
1084         selection->set (touched);
1085         commit_reversible_command ();
1086
1087 }
1088
1089 void
1090 Editor::select_all_selectables_using_loop()
1091 {
1092         Location* location = session->locations()->auto_loop_location();
1093         list<Selectable *> touched;
1094
1095         if (location == 0 || (location->end() - location->start() <= 1))  {
1096                 return;
1097         }
1098
1099
1100         TrackSelection* ts;
1101
1102         if (selection->tracks.empty()) {
1103                 ts = &track_views;
1104         } else {
1105                 ts = &selection->tracks;
1106         }
1107
1108         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1109                 if ((*iter)->hidden()) {
1110                         continue;
1111                 }
1112                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1113         }
1114         begin_reversible_command (_("select all from loop"));
1115         selection->set (touched);
1116         commit_reversible_command ();
1117
1118 }
1119
1120 void
1121 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1122 {
1123         nframes_t start;
1124         nframes_t end;
1125         list<Selectable *> touched;
1126
1127         if (after) {
1128                 begin_reversible_command (_("select all after cursor"));
1129                 start = cursor->current_frame ;
1130                 end = session->current_end_frame();
1131         } else {
1132                 if (cursor->current_frame > 0) {
1133                         begin_reversible_command (_("select all before cursor"));
1134                         start = 0;
1135                         end = cursor->current_frame - 1;
1136                 } else {
1137                         return;
1138                 }
1139         }
1140
1141
1142         TrackSelection* ts;
1143
1144         if (selection->tracks.empty()) {
1145                 ts = &track_views;
1146         } else {
1147                 ts = &selection->tracks;
1148         }
1149
1150         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1151                 if ((*iter)->hidden()) {
1152                         continue;
1153                 }
1154                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1155         }
1156         selection->set (touched);
1157         commit_reversible_command ();
1158 }
1159
1160 void
1161 Editor::select_all_selectables_using_edit (bool after)
1162 {
1163         nframes_t start;
1164         nframes_t end;
1165         list<Selectable *> touched;
1166
1167         if (after) {
1168                 begin_reversible_command (_("select all after edit"));
1169                 start = get_preferred_edit_position();
1170                 end = session->current_end_frame();
1171         } else {
1172                 if ((end = get_preferred_edit_position()) > 1) {
1173                         begin_reversible_command (_("select all before edit"));
1174                         start = 0;
1175                         end -= 1;
1176                 } else {
1177                         return;
1178                 }
1179         }
1180
1181
1182         TrackSelection* ts;
1183
1184         if (selection->tracks.empty()) {
1185                 ts = &track_views;
1186         } else {
1187                 ts = &selection->tracks;
1188         }
1189
1190         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1191                 if ((*iter)->hidden()) {
1192                         continue;
1193                 }
1194                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1195         }
1196         selection->set (touched);
1197         commit_reversible_command ();
1198 }
1199
1200 void
1201 Editor::select_all_selectables_between (bool within)
1202 {
1203         nframes64_t start;
1204         nframes64_t end;
1205         list<Selectable *> touched;
1206
1207         if (!get_edit_op_range (start, end)) {
1208                 return;
1209         }
1210
1211         TrackSelection* ts;
1212
1213         if (selection->tracks.empty()) {
1214                 ts = &track_views;
1215         } else {
1216                 ts = &selection->tracks;
1217         }
1218
1219         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1220                 if ((*iter)->hidden()) {
1221                         continue;
1222                 }
1223                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1224         }
1225
1226         selection->set (touched);
1227 }
1228
1229 void
1230 Editor::select_range_between ()
1231 {
1232         nframes64_t start;
1233         nframes64_t end;
1234         
1235         if (!get_edit_op_range (start, end)) {
1236                 return;
1237         }
1238
1239         set_mouse_mode (MouseRange);
1240         selection->set ((TimeAxisView*) 0, start, end);
1241 }
1242
1243 bool
1244 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1245 {
1246         nframes64_t m;
1247         bool ignored;
1248
1249         /* in range mode, use any existing selection */
1250
1251         if (mouse_mode == MouseRange && !selection->time.empty()) {
1252                 /* we know that these are ordered */
1253                 start = selection->time.start();
1254                 end = selection->time.end_frame();
1255                 return true;
1256         }
1257
1258         if (!mouse_frame (m, ignored)) {
1259                 /* mouse is not in a canvas, try playhead+selected marker.
1260                    this is probably most true when using menus.
1261                  */
1262
1263                 if (selection->markers.empty()) {
1264                         return false;
1265                 }
1266
1267                 start = selection->markers.front()->position();
1268                 end = session->audible_frame();
1269
1270         } else {
1271
1272                 switch (_edit_point) {
1273                 case EditAtPlayhead:
1274                         if (selection->markers.empty()) {
1275                                 /* use mouse + playhead */
1276                                 start = m;
1277                                 end = session->audible_frame();
1278                         } else {
1279                                 /* use playhead + selected marker */
1280                                 start = session->audible_frame();
1281                                 end = selection->markers.front()->position();
1282                         }
1283                         break;
1284                         
1285                 case EditAtMouse:
1286                         /* use mouse + selected marker */
1287                         if (selection->markers.empty()) {
1288                                 start = m;
1289                                 end = session->audible_frame();
1290                         } else {
1291                                 start = selection->markers.front()->position();
1292                                 end = m;
1293                         }
1294                         break;
1295                         
1296                 case EditAtSelectedMarker:
1297                         /* use mouse + selected marker */
1298                         if (selection->markers.empty()) {
1299                                 
1300                                 MessageDialog win (_("No edit range defined"),
1301                                                    false,
1302                                                    MESSAGE_INFO,
1303                                                    BUTTONS_OK);
1304
1305                                 win.set_secondary_text (
1306                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1307                                 
1308
1309                                 win.set_default_response (RESPONSE_CLOSE);
1310                                 win.set_position (Gtk::WIN_POS_MOUSE);
1311                                 win.show_all();
1312                                 
1313                                 win.run ();
1314                                 
1315                                 return false; // NO RANGE
1316                         }
1317                         start = selection->markers.front()->position();
1318                         end = m;
1319                         break;
1320                 }
1321         }
1322
1323         if (start == end) {
1324                 return false;
1325         }
1326
1327         if (start > end) {
1328                 swap (start, end);
1329         }
1330
1331         return true;
1332 }
1333
1334 void
1335 Editor::deselect_all ()
1336 {
1337         selection->clear ();
1338 }
1339
1340