Merge with 2.0-ongoing R2885.
[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
187         switch (op) {
188         case Selection::Toggle:
189                 if (selection->selected (&view)) {
190                         if (!no_remove) {
191                                 selection->remove (&view);
192                         }
193                 } else {
194                         selection->add (&view);
195                 }
196                 break;
197
198         case Selection::Add:
199                 if (!selection->selected (&view)) {
200                         selection->add (&view);
201                 }
202                 break;
203
204         case Selection::Set:
205                 if (selection->selected (&view) && selection->tracks.size() > 1) {
206
207                         /* reset track selection if there is only 1 other track
208                            selected OR if no_remove is not set (its there to 
209                            prevent deselecting a multi-track selection
210                            when clicking on an already selected track
211                            for some reason.
212                         */
213
214                         if (selection->tracks.empty()) {
215                                 selection->set (&view);
216                         } else if (selection->tracks.size() == 1 || !no_remove) {
217                                 selection->set (&view);
218                         }
219                 }
220                 break;
221                 
222         case Selection::Extend:
223                 extend_selection_to_track (view);
224                 break;
225         }
226 }
227
228 void
229 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
230 {
231         if (!clicked_routeview) {
232                 return;
233         }
234         
235         if (!press) {
236                 return;
237         }
238
239         set_selected_track (*clicked_routeview, op, no_remove);
240 }
241
242 bool
243 Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove)
244 {
245         if (!clicked_control_point) {
246                 return false;
247         }
248
249         /* select this point and any others that it represents */
250
251         double y1, y2;
252         nframes_t x1, x2;
253
254         x1 = pixel_to_frame (clicked_control_point->get_x() - 10);
255         x2 = pixel_to_frame (clicked_control_point->get_x() + 10);
256         y1 = clicked_control_point->get_x() - 10;
257         y2 = clicked_control_point->get_y() + 10;
258
259         return select_all_within (x1, x2, y1, y2, selection->tracks, op);
260 }
261
262 void
263 Editor::get_relevant_tracks (set<RouteTimeAxisView*>& relevant_tracks)
264 {
265         /* step one: get all selected tracks and all tracks in the relevant edit groups */
266
267         for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) {
268
269                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*ti);
270
271                 if (!rtv) {
272                         continue;
273                 }
274
275                 RouteGroup* group = rtv->route()->edit_group();
276
277                 if (group && group->is_active()) {
278                         
279                         /* active group for this track, loop over all tracks and get every member of the group */
280
281                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
282                                 
283                                 RouteTimeAxisView* trtv;
284                                 
285                                 if ((trtv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
286                                         
287                                         if (trtv->route()->edit_group() == group) {
288                                                 relevant_tracks.insert (trtv);
289                                         }
290                                 }
291                         }
292                 } else {
293                         relevant_tracks.insert (rtv);
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_tracks (slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis) const
307 {
308         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
309         if (route_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<RouteTimeAxisView*> tracks;
317
318         /* always call for the basis */
319         tracks.insert (route_basis);
320
321         RouteGroup* group = route_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::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
326                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*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<RouteTimeAxisView*>::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) const
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) const
371 {
372         mapover_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_routeview) {
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_routeview)) {
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                                 selection->set (clicked_regionview);
451                                 commit = true;
452                         } else {
453                                 /* no commit necessary: clicked on an already selected region */
454                                 goto out;
455                         }
456                         break;
457
458                 default:
459                         /* silly compiler */
460                         break;
461                 }
462
463         } else if (op == Selection::Extend) {
464
465                 list<Selectable*> results;
466                 nframes_t last_frame;
467                 nframes_t first_frame;
468                 bool same_track = false;
469
470                 /* 1. find the last selected regionview in the track that was clicked in */
471
472                 last_frame = 0;
473                 first_frame = max_frames;
474
475                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
476                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
477
478                                 if ((*x)->region()->last_frame() > last_frame) {
479                                         last_frame = (*x)->region()->last_frame();
480                                 }
481
482                                 if ((*x)->region()->first_frame() < first_frame) {
483                                         first_frame = (*x)->region()->first_frame();
484                                 }
485
486                                 same_track = true;
487                         }
488                 }
489
490                 if (same_track) {
491
492                         /* 2. figure out the boundaries for our search for new objects */
493                         
494                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
495                         case OverlapNone:
496                                 if (last_frame < clicked_regionview->region()->first_frame()) {
497                                         first_frame = last_frame;
498                                         last_frame = clicked_regionview->region()->last_frame();
499                                 } else {
500                                         last_frame = first_frame;
501                                         first_frame = clicked_regionview->region()->first_frame();
502                                 }
503                                 break;
504                                 
505                         case OverlapExternal:
506                                 if (last_frame < clicked_regionview->region()->first_frame()) {
507                                         first_frame = last_frame;
508                                         last_frame = clicked_regionview->region()->last_frame();
509                                 } else {
510                                         last_frame = first_frame;
511                                         first_frame = clicked_regionview->region()->first_frame();
512                                 }
513                                 break;
514                                 
515                         case OverlapInternal:
516                                 if (last_frame < clicked_regionview->region()->first_frame()) {
517                                         first_frame = last_frame;
518                                         last_frame = clicked_regionview->region()->last_frame();
519                                 } else {
520                                         last_frame = first_frame;
521                                         first_frame = clicked_regionview->region()->first_frame();
522                                 }
523                                 break;
524                                 
525                         case OverlapStart:
526                         case OverlapEnd:
527                                 /* nothing to do except add clicked region to selection, since it
528                                    overlaps with the existing selection in this track.
529                                 */
530                                 break;
531                         }
532
533                 } else {
534
535                         /* click in a track that has no regions selected, so extend vertically
536                            to pick out all regions that are defined by the existing selection
537                            plus this one.
538                         */
539                         
540                         
541                         first_frame = entered_regionview->region()->position();
542                         last_frame = entered_regionview->region()->last_frame();
543                         
544                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
545                                 if ((*i)->region()->position() < first_frame) {
546                                         first_frame = (*i)->region()->position();
547                                 }
548                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
549                                         last_frame = (*i)->region()->last_frame();
550                                 }
551                         }
552                 }
553
554                 /* 2. find all the tracks we should select in */
555
556                 set<RouteTimeAxisView*> relevant_tracks;
557                 set<RouteTimeAxisView*> already_in_selection;
558
559                 get_relevant_tracks (relevant_tracks);
560
561                 if (relevant_tracks.empty()) {
562
563                         /* no relevant tracks -> no tracks selected .. thus .. if
564                            the regionview we're in isn't selected (i.e. we're
565                            about to extend to it), then find all tracks between
566                            the this one and any selected ones.
567                         */
568
569                         if (!selection->selected (entered_regionview)) {
570
571                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
572
573                                 if (rtv) {
574
575                                         /* add this track to the ones we will search */
576
577                                         relevant_tracks.insert (rtv);
578
579                                         /* find the track closest to this one that
580                                            already a selected region.
581                                         */
582
583                                         RouteTimeAxisView* closest = 0;
584                                         int distance = INT_MAX;
585                                         int key = rtv->route()->order_key ("editor");
586
587                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
588
589                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
590
591                                                 if (artv && artv != rtv) {
592
593                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
594
595                                                         result = already_in_selection.insert (artv);
596
597                                                         if (result.second) {
598                                                                 /* newly added to already_in_selection */
599                                                         
600
601                                                                 int d = artv->route()->order_key ("editor");
602                                                                 
603                                                                 d -= key;
604                                                                 
605                                                                 if (abs (d) < distance) {
606                                                                         distance = abs (d);
607                                                                         closest = artv;
608                                                                 }
609                                                         }
610                                                 }
611                                         }
612                                         
613                                         if (closest) {
614
615                                                 /* now add all tracks between that one and this one */
616                                                 
617                                                 int okey = closest->route()->order_key ("editor");
618                                                 
619                                                 if (okey > key) {
620                                                         swap (okey, key);
621                                                 }
622                                                 
623                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
624                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
625                                                         if (artv && artv != rtv) {
626
627                                                                 int k = artv->route()->order_key ("editor");
628
629                                                                 if (k >= okey && k <= key) {
630
631                                                                         /* in range but don't add it if
632                                                                            it already has tracks selected.
633                                                                            this avoids odd selection
634                                                                            behaviour that feels wrong.
635                                                                         */
636
637                                                                         if (find (already_in_selection.begin(),
638                                                                                   already_in_selection.end(),
639                                                                                   artv) == already_in_selection.end()) {
640
641                                                                                 relevant_tracks.insert (artv);
642                                                                         }
643                                                                 }
644                                                         }
645                                                 }
646                                         }
647                                 }
648                         }
649                 }
650
651                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
652                            one that was clicked.
653                 */
654
655                 get_relevant_tracks (relevant_tracks);
656
657                 for (set<RouteTimeAxisView*>::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 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         begin_reversible_command (_("set selected regions"));
696         
697         switch (op) {
698         case Selection::Toggle:
699                 /* XXX this is not correct */
700                 selection->toggle (all_equivalent_regions);
701                 break;
702         case Selection::Set:
703                 selection->set (all_equivalent_regions);
704                 break;
705         case Selection::Extend:
706                 selection->add (all_equivalent_regions);
707                 break;
708         case Selection::Add:
709                 selection->add (all_equivalent_regions);
710                 break;
711         }
712
713         commit_reversible_command () ;
714 }
715
716 bool
717 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
718 {
719         RegionView* rv;
720         boost::shared_ptr<Region> r (weak_r.lock());
721
722         if (!r) {
723                 return true;
724         }
725
726         if ((rv = sv->find_view (r)) == 0) {
727                 return true;
728         }
729
730         /* don't reset the selection if its something other than 
731            a single other region.
732         */
733
734         if (selection->regions.size() > 1) {
735                 return true;
736         }
737         
738         begin_reversible_command (_("set selected regions"));
739         
740         selection->set (rv);
741
742         commit_reversible_command () ;
743
744         return true;
745 }
746
747 void
748 Editor::track_selection_changed ()
749 {
750         switch (selection->tracks.size()){
751         case 0:
752                 break;
753         default:
754                 set_selected_mixer_strip (*(selection->tracks.front()));
755                 break;
756         }
757
758         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
759                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
760                         (*i)->set_selected (true);
761                 } else {
762                         (*i)->set_selected (false);
763                 }
764         }
765 }
766
767 void
768 Editor::time_selection_changed ()
769 {
770         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
771                 (*i)->hide_selection ();
772         }
773
774         if (selection->tracks.empty()) {
775                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
776                         (*i)->show_selection (selection->time);
777                 }
778         } else {
779                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
780                         (*i)->show_selection (selection->time);
781                 }
782         }
783
784         if (selection->time.empty()) {
785                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
786         } else {
787                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
788         }
789
790 }
791
792 void
793 Editor::region_selection_changed ()
794 {
795         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
796                 (*i)->set_selected_regionviews (selection->regions);
797         }
798         
799         zoomed_to_region = false;
800 }
801
802 void
803 Editor::point_selection_changed ()
804 {
805         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
806                 (*i)->set_selected_points (selection->points);
807         }
808 }
809
810 void
811 Editor::select_all_in_track (Selection::Operation op)
812 {
813         list<Selectable *> touched;
814
815         if (!clicked_routeview) {
816                 return;
817         }
818         
819         clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
820
821         switch (op) {
822         case Selection::Toggle:
823                 selection->add (touched);
824                 break;
825         case Selection::Set:
826                 selection->set (touched);
827                 break;
828         case Selection::Extend:
829                 /* meaningless, because we're selecting everything */
830                 break;
831         case Selection::Add:
832                 selection->add (touched);
833                 break;
834         }
835 }
836
837 void
838 Editor::select_all (Selection::Operation op)
839 {
840         list<Selectable *> touched;
841
842         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
843                 if ((*iter)->hidden()) {
844                         continue;
845                 }
846                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
847         }
848         begin_reversible_command (_("select all"));
849         switch (op) {
850         case Selection::Add:
851                 selection->add (touched);
852                 break;
853         case Selection::Toggle:
854                 selection->add (touched);
855                 break;
856         case Selection::Set:
857                 selection->set (touched);
858                 break;
859         case Selection::Extend:
860                 /* meaningless, because we're selecting everything */
861                 break;
862         }
863         commit_reversible_command ();
864 }
865 void
866 Editor::invert_selection_in_track ()
867 {
868         list<Selectable *> touched;
869
870         if (!clicked_routeview) {
871                 return;
872         }
873         
874         clicked_routeview->get_inverted_selectables (*selection, touched);
875         selection->set (touched);
876 }
877
878 void
879 Editor::invert_selection ()
880 {
881         list<Selectable *> touched;
882         
883         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
884                 if ((*iter)->hidden()) {
885                         continue;
886                 }
887                 (*iter)->get_inverted_selectables (*selection, touched);
888         }
889
890         selection->set (touched);
891 }
892
893 bool
894 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
895 {
896         list<Selectable*> touched;
897         list<Selectable*>::size_type n = 0;
898         TrackViewList touched_tracks;
899
900         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
901                 if ((*iter)->hidden()) {
902                         continue;
903                 }
904
905                 n = touched.size();
906
907                 (*iter)->get_selectables (start, end, top, bot, touched);
908                 
909                 if (n != touched.size()) {
910                         touched_tracks.push_back (*iter);
911                 }
912         }
913
914         if (touched.empty()) {
915                 return false;
916         }
917
918         if (!touched_tracks.empty()) {
919
920                 switch (op) {
921                 case Selection::Add:
922                         selection->add (touched_tracks);
923                         break;
924                 case Selection::Toggle:
925                         selection->toggle (touched_tracks);
926                         break;
927                 case Selection::Set:
928                         selection->set (touched_tracks);
929                         break;
930                 case Selection::Extend:
931                         /* not defined yet */
932                         break;
933                 }
934         }
935
936         begin_reversible_command (_("select all within"));
937         switch (op) {
938         case Selection::Add:
939                 selection->add (touched);
940                 break;
941         case Selection::Toggle:
942                 selection->toggle (touched);
943                 break;
944         case Selection::Set:
945                 selection->set (touched);
946                 break;
947         case Selection::Extend:
948                 /* not defined yet */
949                 break;
950         }
951         
952         commit_reversible_command ();
953
954         return !touched.empty();
955 }
956
957 void
958 Editor::set_selection_from_region ()
959 {
960         if (selection->regions.empty()) {
961                 return;
962         }
963
964         RegionView* rv = *(selection->regions.begin());
965         boost::shared_ptr<Region> region = rv->region();
966         
967         begin_reversible_command (_("set selection from region"));
968         selection->set (0, region->position(), region->last_frame());
969         commit_reversible_command ();
970
971         set_mouse_mode (Editing::MouseRange, false);
972 }
973
974 void
975 Editor::set_selection_from_punch()
976 {
977         Location* location;
978
979         if ((location = session->locations()->auto_punch_location()) == 0)  {
980                 return;
981         }
982
983         set_selection_from_range (*location);
984 }
985
986 void
987 Editor::set_selection_from_loop()
988 {
989         Location* location;
990
991         if ((location = session->locations()->auto_loop_location()) == 0)  {
992                 return;
993         }
994         set_selection_from_range (*location);
995 }
996
997 void
998 Editor::set_selection_from_range (Location& loc)
999 {
1000         begin_reversible_command (_("set selection from range"));
1001         selection->set (0, loc.start(), loc.end());
1002         commit_reversible_command ();
1003
1004         set_mouse_mode (Editing::MouseRange, false);
1005 }
1006
1007 void
1008 Editor::select_all_selectables_using_time_selection ()
1009 {
1010         list<Selectable *> touched;
1011
1012         if (selection->time.empty()) {
1013                 return;
1014         }
1015
1016         nframes_t start = selection->time[clicked_selection].start;
1017         nframes_t end = selection->time[clicked_selection].end;
1018
1019         if (end - start < 1)  {
1020                 return;
1021         }
1022
1023         TrackSelection* ts;
1024
1025         if (selection->tracks.empty()) {
1026                 ts = &track_views;
1027         } else {
1028                 ts = &selection->tracks;
1029         }
1030
1031         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1032                 if ((*iter)->hidden()) {
1033                         continue;
1034                 }
1035                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1036         }
1037
1038         begin_reversible_command (_("select all from range"));
1039         selection->set (touched);
1040         commit_reversible_command ();
1041 }
1042
1043
1044 void
1045 Editor::select_all_selectables_using_punch()
1046 {
1047         Location* location = session->locations()->auto_punch_location();
1048         list<Selectable *> touched;
1049
1050         if (location == 0 || (location->end() - location->start() <= 1))  {
1051                 return;
1052         }
1053
1054
1055         TrackSelection* ts;
1056
1057         if (selection->tracks.empty()) {
1058                 ts = &track_views;
1059         } else {
1060                 ts = &selection->tracks;
1061         }
1062
1063         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1064                 if ((*iter)->hidden()) {
1065                         continue;
1066                 }
1067                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1068         }
1069         begin_reversible_command (_("select all from punch"));
1070         selection->set (touched);
1071         commit_reversible_command ();
1072
1073 }
1074
1075 void
1076 Editor::select_all_selectables_using_loop()
1077 {
1078         Location* location = session->locations()->auto_loop_location();
1079         list<Selectable *> touched;
1080
1081         if (location == 0 || (location->end() - location->start() <= 1))  {
1082                 return;
1083         }
1084
1085
1086         TrackSelection* ts;
1087
1088         if (selection->tracks.empty()) {
1089                 ts = &track_views;
1090         } else {
1091                 ts = &selection->tracks;
1092         }
1093
1094         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1095                 if ((*iter)->hidden()) {
1096                         continue;
1097                 }
1098                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1099         }
1100         begin_reversible_command (_("select all from loop"));
1101         selection->set (touched);
1102         commit_reversible_command ();
1103
1104 }
1105
1106 void
1107 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1108 {
1109         nframes_t start;
1110         nframes_t end;
1111         list<Selectable *> touched;
1112
1113         if (after) {
1114                 begin_reversible_command (_("select all after cursor"));
1115                 start = cursor->current_frame ;
1116                 end = session->current_end_frame();
1117         } else {
1118                 if (cursor->current_frame > 0) {
1119                         begin_reversible_command (_("select all before cursor"));
1120                         start = 0;
1121                         end = cursor->current_frame - 1;
1122                 } else {
1123                         return;
1124                 }
1125         }
1126
1127
1128         TrackSelection* ts;
1129
1130         if (selection->tracks.empty()) {
1131                 ts = &track_views;
1132         } else {
1133                 ts = &selection->tracks;
1134         }
1135
1136         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1137                 if ((*iter)->hidden()) {
1138                         continue;
1139                 }
1140                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1141         }
1142         selection->set (touched);
1143         commit_reversible_command ();
1144 }
1145
1146 void
1147 Editor::select_all_selectables_using_edit (bool after)
1148 {
1149         nframes_t start;
1150         nframes_t end;
1151         list<Selectable *> touched;
1152
1153         if (after) {
1154                 begin_reversible_command (_("select all after edit"));
1155                 start = get_preferred_edit_position();
1156                 end = session->current_end_frame();
1157         } else {
1158                 if ((end = get_preferred_edit_position()) > 1) {
1159                         begin_reversible_command (_("select all before edit"));
1160                         start = 0;
1161                         end -= 1;
1162                 } else {
1163                         return;
1164                 }
1165         }
1166
1167
1168         TrackSelection* ts;
1169
1170         if (selection->tracks.empty()) {
1171                 ts = &track_views;
1172         } else {
1173                 ts = &selection->tracks;
1174         }
1175
1176         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1177                 if ((*iter)->hidden()) {
1178                         continue;
1179                 }
1180                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1181         }
1182         selection->set (touched);
1183         commit_reversible_command ();
1184 }
1185
1186 void
1187 Editor::select_all_selectables_between (bool within)
1188 {
1189         nframes64_t start;
1190         nframes64_t end;
1191         list<Selectable *> touched;
1192
1193         if (!get_edit_op_range (start, end)) {
1194                 return;
1195         }
1196
1197         TrackSelection* ts;
1198
1199         if (selection->tracks.empty()) {
1200                 ts = &track_views;
1201         } else {
1202                 ts = &selection->tracks;
1203         }
1204
1205         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1206                 if ((*iter)->hidden()) {
1207                         continue;
1208                 }
1209                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1210         }
1211
1212         selection->set (touched);
1213 }
1214
1215 void
1216 Editor::select_range_between ()
1217 {
1218         nframes64_t start;
1219         nframes64_t end;
1220         
1221         if (!get_edit_op_range (start, end)) {
1222                 return;
1223         }
1224
1225         set_mouse_mode (MouseRange);
1226         selection->set ((TimeAxisView*) 0, start, end);
1227 }
1228
1229 bool
1230 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1231 {
1232         nframes64_t m;
1233         bool ignored;
1234
1235         /* in range mode, use any existing selection */
1236
1237         if (mouse_mode == MouseRange && !selection->time.empty()) {
1238                 /* we know that these are ordered */
1239                 start = selection->time.start();
1240                 end = selection->time.end_frame();
1241                 return true;
1242         }
1243
1244         if (!mouse_frame (m, ignored)) {
1245                 /* mouse is not in a canvas, try playhead+selected marker.
1246                    this is probably most true when using menus.
1247                  */
1248
1249                 if (selection->markers.empty()) {
1250                         return false;
1251                 }
1252
1253                 start = selection->markers.front()->position();
1254                 end = session->audible_frame();
1255
1256         } else {
1257
1258                 switch (_edit_point) {
1259                 case EditAtPlayhead:
1260                         if (selection->markers.empty()) {
1261                                 /* use mouse + playhead */
1262                                 start = m;
1263                                 end = session->audible_frame();
1264                         } else {
1265                                 /* use playhead + selected marker */
1266                                 start = session->audible_frame();
1267                                 end = selection->markers.front()->position();
1268                         }
1269                         break;
1270                         
1271                 case EditAtMouse:
1272                         /* use mouse + selected marker */
1273                         if (selection->markers.empty()) {
1274                                 start = m;
1275                                 end = session->audible_frame();
1276                         } else {
1277                                 start = selection->markers.front()->position();
1278                                 end = m;
1279                         }
1280                         break;
1281                         
1282                 case EditAtSelectedMarker:
1283                         /* use mouse + selected marker */
1284                         if (selection->markers.empty()) {
1285                                 
1286                                 MessageDialog win (_("No edit range defined"),
1287                                                    false,
1288                                                    MESSAGE_INFO,
1289                                                    BUTTONS_OK);
1290
1291                                 win.set_secondary_text (
1292                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1293                                 
1294
1295                                 win.set_default_response (RESPONSE_CLOSE);
1296                                 win.set_position (Gtk::WIN_POS_MOUSE);
1297                                 win.show_all();
1298                                 
1299                                 win.run ();
1300                                 
1301                                 return false; // NO RANGE
1302                         }
1303                         start = selection->markers.front()->position();
1304                         end = m;
1305                         break;
1306                 }
1307         }
1308
1309         if (start == end) {
1310                 return false;
1311         }
1312
1313         if (start > end) {
1314                 swap (start, end);
1315         }
1316
1317         return true;
1318 }
1319
1320 void
1321 Editor::deselect_all ()
1322 {
1323         selection->clear ();
1324 }
1325
1326 Editor::ExclusiveRegionSelection::ExclusiveRegionSelection (Editor& ed, RegionView* rv)
1327         : editor (ed),
1328           regionview (rv)
1329 {
1330
1331         if (!rv || ed.current_mouse_mode() != Editing::MouseObject) {
1332                 return;
1333         }
1334         
1335         if (ed.get_selection().regions.empty() && !ed.get_selection().selected (rv)) {
1336                 ed.get_selection().set (rv, false);
1337                 remove = true;
1338         } else {
1339                 remove = false;
1340         }
1341 }
1342
1343 Editor::ExclusiveRegionSelection::~ExclusiveRegionSelection ()
1344 {
1345         if (remove) {
1346                 editor.get_selection().remove (regionview);
1347         }
1348 }
1349