Merge libs/ardour and gtk2_ardour with 2.0-ongoing R2837.
[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 void
717 Editor::track_selection_changed ()
718 {
719         switch (selection->tracks.size()){
720         case 0:
721                 break;
722         default:
723                 set_selected_mixer_strip (*(selection->tracks.front()));
724                 break;
725         }
726
727         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
728                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
729                         (*i)->set_selected (true);
730                 } else {
731                         (*i)->set_selected (false);
732                 }
733         }
734 }
735
736 void
737 Editor::time_selection_changed ()
738 {
739         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
740                 (*i)->hide_selection ();
741         }
742
743         if (selection->tracks.empty()) {
744                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
745                         (*i)->show_selection (selection->time);
746                 }
747         } else {
748                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
749                         (*i)->show_selection (selection->time);
750                 }
751         }
752
753         if (selection->time.empty()) {
754                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
755         } else {
756                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
757         }
758
759 }
760
761 void
762 Editor::region_selection_changed ()
763 {
764         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
765                 (*i)->set_selected_regionviews (selection->regions);
766         }
767         
768         zoomed_to_region = false;
769 }
770
771 void
772 Editor::point_selection_changed ()
773 {
774         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
775                 (*i)->set_selected_points (selection->points);
776         }
777 }
778
779 /** Select everything in the selected tracks
780  * @param Selection operation to apply.
781  */
782 void
783 Editor::select_all_in_selected_tracks (Selection::Operation op)
784 {
785         list<Selectable *> touched;
786
787         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
788                 (*i)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
789         }
790
791         switch (op) {
792         case Selection::Toggle:
793                 selection->add (touched);
794                 break;
795         case Selection::Set:
796                 selection->set (touched);
797                 break;
798         case Selection::Extend:
799                 /* meaningless, because we're selecting everything */
800                 break;
801         case Selection::Add:
802                 selection->add (touched);
803                 break;
804         }
805 }
806
807 void
808 Editor::select_all (Selection::Operation op)
809 {
810         list<Selectable *> touched;
811         
812         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
813                 if ((*iter)->hidden()) {
814                         continue;
815                 }
816                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
817         }
818         begin_reversible_command (_("select all"));
819         switch (op) {
820         case Selection::Add:
821                 selection->add (touched);
822                 break;
823         case Selection::Toggle:
824                 selection->add (touched);
825                 break;
826         case Selection::Set:
827                 selection->set (touched);
828                 break;
829         case Selection::Extend:
830                 /* meaningless, because we're selecting everything */
831                 break;
832         }
833         commit_reversible_command ();
834 }
835
836 /** Invert the selection in the selected tracks */
837 void
838 Editor::invert_selection_in_selected_tracks ()
839 {
840         list<Selectable *> touched;
841
842         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
843                 (*i)->get_inverted_selectables (*selection, touched);
844         }
845         
846         selection->set (touched);
847 }
848
849 void
850 Editor::invert_selection ()
851 {
852         list<Selectable *> touched;
853         
854         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
855                 if ((*iter)->hidden()) {
856                         continue;
857                 }
858                 (*iter)->get_inverted_selectables (*selection, touched);
859         }
860
861         selection->set (touched);
862 }
863
864 bool
865 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
866 {
867         list<Selectable*> touched;
868         list<Selectable*>::size_type n = 0;
869         TrackViewList touched_tracks;
870
871         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
872                 if ((*iter)->hidden()) {
873                         continue;
874                 }
875
876                 n = touched.size();
877
878                 (*iter)->get_selectables (start, end, top, bot, touched);
879                 
880                 if (n != touched.size()) {
881                         touched_tracks.push_back (*iter);
882                 }
883         }
884
885         if (touched.empty()) {
886                 return false;
887         }
888
889         if (!touched_tracks.empty()) {
890
891                 switch (op) {
892                 case Selection::Add:
893                         selection->add (touched_tracks);
894                         break;
895                 case Selection::Toggle:
896                         selection->toggle (touched_tracks);
897                         break;
898                 case Selection::Set:
899                         selection->set (touched_tracks);
900                         break;
901                 case Selection::Extend:
902                         /* not defined yet */
903                         break;
904                 }
905         }
906
907         begin_reversible_command (_("select all within"));
908         switch (op) {
909         case Selection::Add:
910                 selection->add (touched);
911                 break;
912         case Selection::Toggle:
913                 selection->toggle (touched);
914                 break;
915         case Selection::Set:
916                 selection->set (touched);
917                 break;
918         case Selection::Extend:
919                 /* not defined yet */
920                 break;
921         }
922         
923         commit_reversible_command ();
924
925         return !touched.empty();
926 }
927
928 void
929 Editor::set_selection_from_audio_region ()
930 {
931         if (selection->regions.empty()) {
932                 return;
933         }
934
935         RegionView* rv = *(selection->regions.begin());
936         boost::shared_ptr<Region> region = rv->region();
937         
938         begin_reversible_command (_("set selection from region"));
939         selection->set (0, region->position(), region->last_frame());
940         commit_reversible_command ();
941
942         set_mouse_mode (Editing::MouseRange, false);
943 }
944
945 void
946 Editor::set_selection_from_punch()
947 {
948         Location* location;
949
950         if ((location = session->locations()->auto_punch_location()) == 0)  {
951                 return;
952         }
953
954         set_selection_from_range (*location);
955 }
956
957 void
958 Editor::set_selection_from_loop()
959 {
960         Location* location;
961
962         if ((location = session->locations()->auto_loop_location()) == 0)  {
963                 return;
964         }
965         set_selection_from_range (*location);
966 }
967
968 void
969 Editor::set_selection_from_range (Location& loc)
970 {
971         begin_reversible_command (_("set selection from range"));
972         selection->set (0, loc.start(), loc.end());
973         commit_reversible_command ();
974
975         set_mouse_mode (Editing::MouseRange, false);
976 }
977
978 void
979 Editor::select_all_selectables_using_time_selection ()
980 {
981         list<Selectable *> touched;
982
983         if (selection->time.empty()) {
984                 return;
985         }
986
987         nframes_t start = selection->time[clicked_selection].start;
988         nframes_t end = selection->time[clicked_selection].end;
989
990         if (end - start < 1)  {
991                 return;
992         }
993
994         for (TrackViewList::iterator iter = selection->tracks.begin(); iter != selection->tracks.end(); ++iter) {
995                 if ((*iter)->hidden()) {
996                         continue;
997                 }
998                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
999         }
1000
1001         begin_reversible_command (_("select all from range"));
1002         selection->set (touched);
1003         commit_reversible_command ();
1004 }
1005
1006
1007 void
1008 Editor::select_all_selectables_using_punch()
1009 {
1010         Location* location = session->locations()->auto_punch_location();
1011         list<Selectable *> touched;
1012
1013         if (location == 0 || (location->end() - location->start() <= 1))  {
1014                 return;
1015         }
1016
1017         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1018                 if ((*iter)->hidden()) {
1019                         continue;
1020                 }
1021                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1022         }
1023         begin_reversible_command (_("select all from punch"));
1024         selection->set (touched);
1025         commit_reversible_command ();
1026
1027 }
1028
1029 void
1030 Editor::select_all_selectables_using_loop()
1031 {
1032         Location* location = session->locations()->auto_loop_location();
1033         list<Selectable *> touched;
1034
1035         if (location == 0 || (location->end() - location->start() <= 1))  {
1036                 return;
1037         }
1038
1039         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1040                 if ((*iter)->hidden()) {
1041                         continue;
1042                 }
1043                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1044         }
1045         begin_reversible_command (_("select all from loop"));
1046         selection->set (touched);
1047         commit_reversible_command ();
1048
1049 }
1050
1051 void
1052 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1053 {
1054         nframes_t start;
1055         nframes_t end;
1056         list<Selectable *> touched;
1057
1058         if (after) {
1059                 begin_reversible_command (_("select all after cursor"));
1060                 start = cursor->current_frame ;
1061                 end = session->current_end_frame();
1062         } else {
1063                 if (cursor->current_frame > 0) {
1064                         begin_reversible_command (_("select all before cursor"));
1065                         start = 0;
1066                         end = cursor->current_frame - 1;
1067                 } else {
1068                         return;
1069                 }
1070         }
1071
1072         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1073                 if ((*iter)->hidden()) {
1074                         continue;
1075                 }
1076                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1077         }
1078         selection->set (touched);
1079         commit_reversible_command ();
1080 }
1081
1082 void
1083 Editor::select_all_selectables_using_edit (bool after)
1084 {
1085         nframes_t start;
1086         nframes_t end;
1087         list<Selectable *> touched;
1088
1089         if (after) {
1090                 begin_reversible_command (_("select all after edit"));
1091                 start = get_preferred_edit_position();
1092                 end = session->current_end_frame();
1093         } else {
1094                 if ((end = get_preferred_edit_position()) > 1) {
1095                         begin_reversible_command (_("select all before edit"));
1096                         start = 0;
1097                         end -= 1;
1098                 } else {
1099                         return;
1100                 }
1101         }
1102
1103         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1104                 if ((*iter)->hidden()) {
1105                         continue;
1106                 }
1107                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1108         }
1109         selection->set (touched);
1110         commit_reversible_command ();
1111 }
1112
1113 void
1114 Editor::select_all_selectables_between (bool within)
1115 {
1116         nframes64_t start;
1117         nframes64_t end;
1118         list<Selectable *> touched;
1119
1120         if (!get_edit_op_range (start, end)) {
1121                 return;
1122         }
1123         
1124         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1125                 if ((*iter)->hidden()) {
1126                         continue;
1127                 }
1128                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1129         }
1130
1131         selection->set (touched);
1132 }
1133
1134 void
1135 Editor::select_range_between ()
1136 {
1137         nframes64_t start;
1138         nframes64_t end;
1139         
1140         if (!get_edit_op_range (start, end)) {
1141                 return;
1142         }
1143
1144         set_mouse_mode (MouseRange);
1145         selection->set ((TimeAxisView*) 0, start, end);
1146 }
1147
1148 bool
1149 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1150 {
1151         nframes64_t m;
1152         bool ignored;
1153
1154         /* in range mode, use any existing selection */
1155
1156         if (mouse_mode == MouseRange && !selection->time.empty()) {
1157                 /* we know that these are ordered */
1158                 start = selection->time.start();
1159                 end = selection->time.end_frame();
1160                 return true;
1161         }
1162
1163         if (!mouse_frame (m, ignored)) {
1164                 /* mouse is not in a canvas, try playhead+selected marker.
1165                    this is probably most true when using menus.
1166                  */
1167
1168                 if (selection->markers.empty()) {
1169                         return false;
1170                 }
1171
1172                 start = selection->markers.front()->position();
1173                 end = session->audible_frame();
1174
1175         } else {
1176
1177                 switch (_edit_point) {
1178                 case EditAtPlayhead:
1179                         if (selection->markers.empty()) {
1180                                 /* use mouse + playhead */
1181                                 start = m;
1182                                 end = session->audible_frame();
1183                         } else {
1184                                 /* use playhead + selected marker */
1185                                 start = session->audible_frame();
1186                                 end = selection->markers.front()->position();
1187                         }
1188                         break;
1189                         
1190                 case EditAtMouse:
1191                         /* use mouse + selected marker */
1192                         if (selection->markers.empty()) {
1193                                 start = m;
1194                                 end = session->audible_frame();
1195                         } else {
1196                                 start = selection->markers.front()->position();
1197                                 end = m;
1198                         }
1199                         break;
1200                         
1201                 case EditAtSelectedMarker:
1202                         /* use mouse + selected marker */
1203                         if (selection->markers.empty()) {
1204                                 
1205                                 MessageDialog win (_("No edit range defined"),
1206                                                    false,
1207                                                    MESSAGE_INFO,
1208                                                    BUTTONS_OK);
1209
1210                                 win.set_secondary_text (
1211                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1212                                 
1213
1214                                 win.set_default_response (RESPONSE_CLOSE);
1215                                 win.set_position (Gtk::WIN_POS_MOUSE);
1216                                 win.show_all();
1217                                 
1218                                 win.run ();
1219                                 
1220                                 return false; // NO RANGE
1221                         }
1222                         start = selection->markers.front()->position();
1223                         end = m;
1224                         break;
1225                 }
1226         }
1227
1228         if (start == end) {
1229                 return false;
1230         }
1231
1232         if (start > end) {
1233                 swap (start, end);
1234         }
1235
1236         return true;
1237 }
1238
1239 void
1240 Editor::deselect_all ()
1241 {
1242         selection->clear ();
1243 }