adjusting region fade in/out lengths makes the fade in/out active; new font sizes...
[ardour.git] / gtk2_ardour / editor_selection.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <stdlib.h>
22
23 #include <pbd/stacktrace.h>
24
25 #include <ardour/diskstream.h>
26 #include <ardour/playlist.h>
27 #include <ardour/route_group.h>
28
29 #include "editor.h"
30 #include "actions.h"
31 #include "audio_time_axis.h"
32 #include "audio_region_view.h"
33 #include "audio_streamview.h"
34 #include "automation_line.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace sigc;
40 using namespace ARDOUR;
41 using namespace PBD;
42 using namespace Gtk;
43 using namespace Glib;
44 using namespace Gtkmm2ext;
45 using namespace Editing;
46
47 struct TrackViewByPositionSorter
48 {
49     bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
50             return a->y_position < b->y_position;
51     }
52 };
53
54 bool
55 Editor::extend_selection_to_track (TimeAxisView& view)
56 {
57         if (selection->selected (&view)) {
58                 /* already selected, do nothing */
59                 return false;
60         }
61
62         if (selection->tracks.empty()) {
63
64                 if (!selection->selected (&view)) {
65                         selection->set (&view);
66                         return true;
67                 } else {
68                         return false;
69                 }
70         } 
71
72         /* something is already selected, so figure out which range of things to add */
73         
74         TrackViewList to_be_added;
75         TrackViewList sorted = track_views;
76         TrackViewByPositionSorter cmp;
77         bool passed_clicked = false;
78         bool forwards = true;
79
80         sorted.sort (cmp);
81
82         if (!selection->selected (&view)) {
83                 to_be_added.push_back (&view);
84         }
85
86         /* figure out if we should go forward or backwards */
87
88         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
89
90                 if ((*i) == &view) {
91                         passed_clicked = true;
92                 }
93
94                 if (selection->selected (*i)) {
95                         if (passed_clicked) {
96                                 forwards = true;
97                         } else {
98                                 forwards = false;
99                         }
100                         break;
101                 }
102         }
103                         
104         passed_clicked = false;
105
106         if (forwards) {
107
108                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
109                                         
110                         if ((*i) == &view) {
111                                 passed_clicked = true;
112                                 continue;
113                         }
114                                         
115                         if (passed_clicked) {
116                                 if ((*i)->hidden()) {
117                                         continue;
118                                 }
119                                 if (selection->selected (*i)) {
120                                         break;
121                                 } else if (!(*i)->hidden()) {
122                                         to_be_added.push_back (*i);
123                                 }
124                         }
125                 }
126
127         } else {
128
129                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
130                                         
131                         if ((*r) == &view) {
132                                 passed_clicked = true;
133                                 continue;
134                         }
135                                         
136                         if (passed_clicked) {
137                                                 
138                                 if ((*r)->hidden()) {
139                                         continue;
140                                 }
141                                                 
142                                 if (selection->selected (*r)) {
143                                         break;
144                                 } else if (!(*r)->hidden()) {
145                                         to_be_added.push_back (*r);
146                                 }
147                         }
148                 }
149         }
150                         
151         if (!to_be_added.empty()) {
152                 selection->add (to_be_added);
153                 return true;
154         }
155         
156         return false;
157 }
158
159 void
160 Editor::select_all_tracks ()
161 {
162         selection->set (track_views);
163 }
164
165 void
166 Editor::set_selected_track_as_side_effect (bool force)
167 {
168         if (!clicked_trackview) {
169                 return;
170         }
171
172         if (!selection->tracks.empty()) {
173                 if (!selection->selected (clicked_trackview)) {
174                         selection->add (clicked_trackview);
175                 }
176
177         } else if (force) {
178                 selection->set (clicked_trackview);
179         }
180 }
181
182 void
183 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
184 {
185
186         cerr << "set selected track, op = " << op << " selected ? " << selection->selected (&view) << " no remove? " << no_remove << endl;
187
188         switch (op) {
189         case Selection::Toggle:
190                 if (selection->selected (&view)) {
191                         if (!no_remove) {
192                                 selection->remove (&view);
193                         }
194                 } else {
195                         selection->add (&view);
196                 }
197                 break;
198
199         case Selection::Add:
200                 if (!selection->selected (&view)) {
201                         selection->add (&view);
202                 }
203                 break;
204
205         case Selection::Set:
206                 if (selection->selected (&view) && selection->tracks.size() > 1) {
207
208                         /* reset track selection if there is only 1 other track
209                            selected OR if no_remove is not set (its there to 
210                            prevent deselecting a multi-track selection
211                            when clicking on an already selected track
212                            for some reason.
213                         */
214
215                         if (selection->tracks.empty()) {
216                                 selection->set (&view);
217                         } else if (selection->tracks.size() == 1 || !no_remove) {
218                                 selection->set (&view);
219                         }
220                 }
221                 break;
222                 
223         case Selection::Extend:
224                 extend_selection_to_track (view);
225                 break;
226         }
227 }
228
229 void
230 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
231 {
232         if (!clicked_trackview) {
233                 return;
234         }
235         
236         if (!press) {
237                 return;
238         }
239
240         set_selected_track (*clicked_trackview, op, no_remove);
241 }
242
243 bool
244 Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove)
245 {
246         if (!clicked_control_point) {
247                 return false;
248         }
249
250         /* select this point and any others that it represents */
251
252         double y1, y2;
253         nframes_t x1, x2;
254
255         x1 = pixel_to_frame (clicked_control_point->get_x() - 10);
256         x2 = pixel_to_frame (clicked_control_point->get_x() + 10);
257         y1 = clicked_control_point->get_x() - 10;
258         y2 = clicked_control_point->get_y() + 10;
259
260         return select_all_within (x1, x2, y1, y2, selection->tracks, op);
261 }
262
263 void
264 Editor::get_relevant_audio_tracks (set<AudioTimeAxisView*>& relevant_tracks)
265 {
266         /* step one: get all selected tracks and all tracks in the relevant edit groups */
267
268         for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) {
269
270                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*ti);
271
272                 if (!atv) {
273                         continue;
274                 }
275
276                 RouteGroup* group = atv->route()->edit_group();
277
278                 if (group && group->is_active()) {
279                         
280                         /* active group for this track, loop over all tracks and get every member of the group */
281
282                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
283                                 
284                                 AudioTimeAxisView* tatv;
285                                 
286                                 if ((tatv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
287                                         
288                                         if (tatv->route()->edit_group() == group) {
289                                                 relevant_tracks.insert (tatv);
290                                         }
291                                 }
292                         }
293                 } else {
294                         relevant_tracks.insert (atv);
295                 }
296         }
297 }
298
299
300 /**
301  *  Call a slot for a given `basis' track and also for any track that is in the same
302  *  active edit group.
303  *  @param sl Slot to call.
304  *  @param basis Basis track.
305  */
306
307 void
308 Editor::mapover_audio_tracks (slot<void,AudioTimeAxisView&,uint32_t> sl, TimeAxisView* basis)
309 {
310         AudioTimeAxisView* audio_basis = dynamic_cast<AudioTimeAxisView*> (basis);
311         if (audio_basis == 0) {
312                 return;
313         }
314
315         /* work out the tracks that we will call the slot for; use
316            a set here as it will disallow possible duplicates of the
317            basis track */
318         set<AudioTimeAxisView*> tracks;
319
320         /* always call for the basis */
321         tracks.insert (audio_basis);
322
323         RouteGroup* group = audio_basis->route()->edit_group();
324         if (group && group->is_active()) {
325
326                 /* the basis is a member of an active edit group; find other members */
327                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
328                         AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*> (*i);
329                         if (v && v->route()->edit_group() == group) {
330                                 tracks.insert (v);
331                         }
332                 }
333         }
334
335         /* call the slots */
336         uint32_t const sz = tracks.size ();
337         for (set<AudioTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
338                 sl (**i, sz);
339         }
340 }
341
342 void
343 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t ignored, RegionView* basis, vector<RegionView*>* all_equivs)
344 {
345         boost::shared_ptr<Playlist> pl;
346         vector<boost::shared_ptr<Region> > results;
347         RegionView* marv;
348         boost::shared_ptr<Diskstream> ds;
349
350         if ((ds = tv.get_diskstream()) == 0) {
351                 /* bus */
352                 return;
353         }
354
355         if (&tv == &basis->get_time_axis_view()) {
356                 /* looking in same track as the original */
357                 return;
358         }
359
360         if ((pl = ds->playlist()) != 0) {
361                 pl->get_equivalent_regions (basis->region(), results);
362         }
363
364         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
365                 if ((marv = tv.view()->find_view (*ir)) != 0) {
366                         all_equivs->push_back (marv);
367                 }
368         }
369 }
370
371 void
372 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions)
373 {
374         mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview());
375         
376         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
377         
378         equivalent_regions.push_back (basis);
379 }
380
381 bool
382 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove)
383 {
384         vector<RegionView*> all_equivalent_regions;
385         bool commit = false;
386
387         if (!clicked_regionview || !clicked_audio_trackview) {
388                 return false;
389         }
390
391         if (press) {
392                 button_release_can_deselect = false;
393         } 
394
395         if (op == Selection::Toggle || op == Selection::Set) {
396
397
398                 switch (op) {
399                 case Selection::Toggle:
400                         
401                         if (selection->selected (clicked_regionview)) {
402                                 if (press) {
403
404                                         /* whatever was clicked was selected already; do nothing here but allow
405                                            the button release to deselect it
406                                         */
407
408                                         button_release_can_deselect = true;
409
410                                 } else {
411
412                                         if (button_release_can_deselect) {
413
414                                                 /* just remove this one region, but only on a permitted button release */
415
416                                                 selection->remove (clicked_regionview);
417                                                 commit = true;
418
419                                                 /* no more deselect action on button release till a new press
420                                                    finds an already selected object.
421                                                 */
422
423                                                 button_release_can_deselect = false;
424                                         }
425                                 } 
426
427                         } else {
428
429                                 if (press) {
430
431                                        if (selection->selected (clicked_audio_trackview)) {
432                                                get_equivalent_regions (clicked_regionview, all_equivalent_regions);
433                                        } else {
434                                                all_equivalent_regions.push_back (clicked_regionview);
435                                        }
436
437                                         /* add all the equivalent regions, but only on button press */
438                                         
439
440
441                                         if (!all_equivalent_regions.empty()) {
442                                                 commit = true;
443                                         }
444
445                                         selection->add (all_equivalent_regions);
446                                 } 
447                         }
448                         break;
449                         
450                 case Selection::Set:
451                         if (!selection->selected (clicked_regionview)) {
452
453                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions);
454                                 selection->set (all_equivalent_regions);
455                                 commit = true;
456                         } else {
457                                 /* no commit necessary: clicked on an already selected region */
458                                 goto out;
459                         }
460                         break;
461
462                 default:
463                         /* silly compiler */
464                         break;
465                 }
466
467         } else if (op == Selection::Extend) {
468
469                 list<Selectable*> results;
470                 nframes_t last_frame;
471                 nframes_t first_frame;
472                 bool same_track = false;
473
474                 /* 1. find the last selected regionview in the track that was clicked in */
475
476                 last_frame = 0;
477                 first_frame = max_frames;
478
479                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
480                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
481
482                                 if ((*x)->region()->last_frame() > last_frame) {
483                                         last_frame = (*x)->region()->last_frame();
484                                 }
485
486                                 if ((*x)->region()->first_frame() < first_frame) {
487                                         first_frame = (*x)->region()->first_frame();
488                                 }
489
490                                 same_track = true;
491                         }
492                 }
493
494                 if (same_track) {
495
496                         /* 2. figure out the boundaries for our search for new objects */
497                         
498                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
499                         case OverlapNone:
500                                 if (last_frame < clicked_regionview->region()->first_frame()) {
501                                         first_frame = last_frame;
502                                         last_frame = clicked_regionview->region()->last_frame();
503                                 } else {
504                                         last_frame = first_frame;
505                                         first_frame = clicked_regionview->region()->first_frame();
506                                 }
507                                 break;
508                                 
509                         case OverlapExternal:
510                                 if (last_frame < clicked_regionview->region()->first_frame()) {
511                                         first_frame = last_frame;
512                                         last_frame = clicked_regionview->region()->last_frame();
513                                 } else {
514                                         last_frame = first_frame;
515                                         first_frame = clicked_regionview->region()->first_frame();
516                                 }
517                                 break;
518                                 
519                         case OverlapInternal:
520                                 if (last_frame < clicked_regionview->region()->first_frame()) {
521                                         first_frame = last_frame;
522                                         last_frame = clicked_regionview->region()->last_frame();
523                                 } else {
524                                         last_frame = first_frame;
525                                         first_frame = clicked_regionview->region()->first_frame();
526                                 }
527                                 break;
528                                 
529                         case OverlapStart:
530                         case OverlapEnd:
531                                 /* nothing to do except add clicked region to selection, since it
532                                    overlaps with the existing selection in this track.
533                                 */
534                                 break;
535                         }
536
537                 } else {
538
539                         /* click in a track that has no regions selected, so extend vertically
540                            to pick out all regions that are defined by the existing selection
541                            plus this one.
542                         */
543                         
544                         
545                         first_frame = entered_regionview->region()->position();
546                         last_frame = entered_regionview->region()->last_frame();
547                         
548                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
549                                 if ((*i)->region()->position() < first_frame) {
550                                         first_frame = (*i)->region()->position();
551                                 }
552                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
553                                         last_frame = (*i)->region()->last_frame();
554                                 }
555                         }
556                 }
557
558                 /* 2. find all the tracks we should select in */
559
560                 set<AudioTimeAxisView*> relevant_tracks;
561                 set<AudioTimeAxisView*> already_in_selection;
562
563                 get_relevant_audio_tracks (relevant_tracks);
564
565                 if (relevant_tracks.empty()) {
566
567                         /* no relevant tracks -> no tracks selected .. thus .. if
568                            the regionview we're in isn't selected (i.e. we're
569                            about to extend to it), then find all tracks between
570                            the this one and any selected ones.
571                         */
572
573                         if (!selection->selected (entered_regionview)) {
574
575                                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&entered_regionview->get_time_axis_view());
576
577                                 if (atv) {
578
579                                         /* add this track to the ones we will search */
580
581                                         relevant_tracks.insert (atv);
582
583                                         /* find the track closest to this one that
584                                            already a selected region.
585                                         */
586
587                                         AudioTimeAxisView* closest = 0;
588                                         int distance = INT_MAX;
589                                         int key = atv->route()->order_key ("editor");
590
591                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
592
593                                                 AudioTimeAxisView* aatv = dynamic_cast<AudioTimeAxisView*>(&(*x)->get_time_axis_view());
594
595                                                 if (aatv && aatv != atv) {
596
597                                                         pair<set<AudioTimeAxisView*>::iterator,bool> result;
598
599                                                         result = already_in_selection.insert (aatv);
600
601                                                         if (result.second) {
602                                                                 /* newly added to already_in_selection */
603                                                         
604
605                                                                 int d = aatv->route()->order_key ("editor");
606                                                                 
607                                                                 d -= key;
608                                                                 
609                                                                 if (abs (d) < distance) {
610                                                                         distance = abs (d);
611                                                                         closest = aatv;
612                                                                 }
613                                                         }
614                                                 }
615                                         }
616                                         
617                                         if (closest) {
618
619                                                 /* now add all tracks between that one and this one */
620                                                 
621                                                 int okey = closest->route()->order_key ("editor");
622                                                 
623                                                 if (okey > key) {
624                                                         swap (okey, key);
625                                                 }
626                                                 
627                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
628                                                         AudioTimeAxisView* aatv = dynamic_cast<AudioTimeAxisView*>(*x);
629                                                         if (aatv && aatv != atv) {
630
631                                                                 int k = aatv->route()->order_key ("editor");
632
633                                                                 if (k >= okey && k <= key) {
634
635                                                                         /* in range but don't add it if
636                                                                            it already has tracks selected.
637                                                                            this avoids odd selection
638                                                                            behaviour that feels wrong.
639                                                                         */
640
641                                                                         if (find (already_in_selection.begin(),
642                                                                                   already_in_selection.end(),
643                                                                                   aatv) == already_in_selection.end()) {
644
645                                                                                 relevant_tracks.insert (aatv);
646                                                                         }
647                                                                 }
648                                                         }
649                                                 }
650                                         }
651                                 }
652                         }
653                 }
654
655                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
656                            one that was clicked.
657                 */
658
659                 for (set<AudioTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
660                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
661                 }
662                 
663                 /* 4. convert to a vector of audio regions */
664
665                 vector<RegionView*> regions;
666                 
667                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
668                         RegionView* arv;
669
670                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
671                                 regions.push_back (arv);
672                         }
673                 }
674
675                 if (!regions.empty()) {
676                         selection->add (regions);
677                         commit = true;
678                 }
679         }
680
681   out:
682         return commit;
683 }
684
685
686 void
687 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
688 {
689         vector<RegionView*> all_equivalent_regions;
690
691         get_regions_corresponding_to (region, all_equivalent_regions);
692
693         if (all_equivalent_regions.empty()) {
694                 return;
695         }
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
714 bool
715 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
716 {
717         RegionView* rv;
718         boost::shared_ptr<Region> r (weak_r.lock());
719
720         if (!r) {
721                 return true;
722         }
723
724         boost::shared_ptr<AudioRegion> ar;
725
726         if ((ar = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
727                 return true;
728         }
729
730         if ((rv = sv->find_view (ar)) == 0) {
731                 return true;
732         }
733
734         /* don't reset the selection if its something other than 
735            a single other region.
736         */
737
738         if (selection->regions.size() > 1) {
739                 return true;
740         }
741         
742         begin_reversible_command (_("set selected regions"));
743         
744         selection->set (rv);
745
746         commit_reversible_command () ;
747
748         return true;
749 }
750
751 void
752 Editor::track_selection_changed ()
753 {
754         switch (selection->tracks.size()){
755         case 0:
756                 break;
757         default:
758                 set_selected_mixer_strip (*(selection->tracks.front()));
759                 break;
760         }
761
762         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
763                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
764                         (*i)->set_selected (true);
765                 } else {
766                         (*i)->set_selected (false);
767                 }
768         }
769 }
770
771 void
772 Editor::time_selection_changed ()
773 {
774         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
775                 (*i)->hide_selection ();
776         }
777
778         if (selection->tracks.empty()) {
779                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
780                         (*i)->show_selection (selection->time);
781                 }
782         } else {
783                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
784                         (*i)->show_selection (selection->time);
785                 }
786         }
787
788         if (selection->time.empty()) {
789                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
790         } else {
791                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
792         }
793
794 }
795
796 void
797 Editor::region_selection_changed ()
798 {
799         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
800                 (*i)->set_selected_regionviews (selection->regions);
801         }
802         
803         zoomed_to_region = false;
804 }
805
806 void
807 Editor::point_selection_changed ()
808 {
809         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
810                 (*i)->set_selected_points (selection->points);
811         }
812 }
813
814 void
815 Editor::select_all_in_track (Selection::Operation op)
816 {
817         list<Selectable *> touched;
818
819         if (!clicked_trackview) {
820                 return;
821         }
822         
823         clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
824
825         switch (op) {
826         case Selection::Toggle:
827                 selection->add (touched);
828                 break;
829         case Selection::Set:
830                 selection->set (touched);
831                 break;
832         case Selection::Extend:
833                 /* meaningless, because we're selecting everything */
834                 break;
835         case Selection::Add:
836                 selection->add (touched);
837                 break;
838         }
839 }
840
841 void
842 Editor::select_all (Selection::Operation op)
843 {
844         list<Selectable *> touched;
845
846         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
847                 if ((*iter)->hidden()) {
848                         continue;
849                 }
850                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
851         }
852         begin_reversible_command (_("select all"));
853         switch (op) {
854         case Selection::Add:
855                 selection->add (touched);
856                 break;
857         case Selection::Toggle:
858                 selection->add (touched);
859                 break;
860         case Selection::Set:
861                 selection->set (touched);
862                 break;
863         case Selection::Extend:
864                 /* meaningless, because we're selecting everything */
865                 break;
866         }
867         commit_reversible_command ();
868 }
869
870 void
871 Editor::invert_selection_in_track ()
872 {
873         list<Selectable *> touched;
874
875         if (!clicked_trackview) {
876                 return;
877         }
878         
879         clicked_trackview->get_inverted_selectables (*selection, touched);
880         selection->set (touched);
881 }
882
883 void
884 Editor::invert_selection ()
885 {
886         list<Selectable *> touched;
887         
888         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
889                 if ((*iter)->hidden()) {
890                         continue;
891                 }
892                 (*iter)->get_inverted_selectables (*selection, touched);
893         }
894
895         selection->set (touched);
896 }
897
898 bool
899 Editor::select_all_within (nframes_t start, nframes_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
900 {
901         list<Selectable*> touched;
902         list<Selectable*>::size_type n = 0;
903         TrackViewList touched_tracks;
904
905         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
906                 if ((*iter)->hidden()) {
907                         continue;
908                 }
909
910                 n = touched.size();
911
912                 (*iter)->get_selectables (start, end, top, bot, touched);
913                 
914                 if (n != touched.size()) {
915                         touched_tracks.push_back (*iter);
916                 }
917         }
918
919         if (touched.empty()) {
920                 return false;
921         }
922
923         if (!touched_tracks.empty()) {
924
925                 switch (op) {
926                 case Selection::Add:
927                         selection->add (touched_tracks);
928                         break;
929                 case Selection::Toggle:
930                         selection->toggle (touched_tracks);
931                         break;
932                 case Selection::Set:
933                         selection->set (touched_tracks);
934                         break;
935                 case Selection::Extend:
936                         /* not defined yet */
937                         break;
938                 }
939         }
940
941         begin_reversible_command (_("select all within"));
942         switch (op) {
943         case Selection::Add:
944                 selection->add (touched);
945                 break;
946         case Selection::Toggle:
947                 selection->toggle (touched);
948                 break;
949         case Selection::Set:
950                 selection->set (touched);
951                 break;
952         case Selection::Extend:
953                 /* not defined yet */
954                 break;
955         }
956         
957         commit_reversible_command ();
958
959         return !touched.empty();
960 }
961
962 void
963 Editor::set_selection_from_audio_region ()
964 {
965         if (selection->regions.empty()) {
966                 return;
967         }
968
969         RegionView* rv = *(selection->regions.begin());
970         boost::shared_ptr<Region> region = rv->region();
971         
972         begin_reversible_command (_("set selection from region"));
973         selection->set (0, region->position(), region->last_frame());
974         commit_reversible_command ();
975
976         set_mouse_mode (Editing::MouseRange, false);
977 }
978
979 void
980 Editor::set_selection_from_punch()
981 {
982         Location* location;
983
984         if ((location = session->locations()->auto_punch_location()) == 0)  {
985                 return;
986         }
987
988         set_selection_from_range (*location);
989 }
990
991 void
992 Editor::set_selection_from_loop()
993 {
994         Location* location;
995
996         if ((location = session->locations()->auto_loop_location()) == 0)  {
997                 return;
998         }
999         set_selection_from_range (*location);
1000 }
1001
1002 void
1003 Editor::set_selection_from_range (Location& loc)
1004 {
1005         begin_reversible_command (_("set selection from range"));
1006         selection->set (0, loc.start(), loc.end());
1007         commit_reversible_command ();
1008
1009         set_mouse_mode (Editing::MouseRange, false);
1010 }
1011
1012 void
1013 Editor::select_all_selectables_using_time_selection ()
1014 {
1015         list<Selectable *> touched;
1016
1017         if (selection->time.empty()) {
1018                 return;
1019         }
1020
1021         nframes_t start = selection->time[clicked_selection].start;
1022         nframes_t end = selection->time[clicked_selection].end;
1023
1024         if (end - start < 1)  {
1025                 return;
1026         }
1027
1028         TrackSelection* ts;
1029
1030         if (selection->tracks.empty()) {
1031                 ts = &track_views;
1032         } else {
1033                 ts = &selection->tracks;
1034         }
1035
1036         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1037                 if ((*iter)->hidden()) {
1038                         continue;
1039                 }
1040                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1041         }
1042
1043         begin_reversible_command (_("select all from range"));
1044         selection->set (touched);
1045         commit_reversible_command ();
1046 }
1047
1048
1049 void
1050 Editor::select_all_selectables_using_punch()
1051 {
1052         Location* location = session->locations()->auto_punch_location();
1053         list<Selectable *> touched;
1054
1055         if (location == 0 || (location->end() - location->start() <= 1))  {
1056                 return;
1057         }
1058
1059
1060         TrackSelection* ts;
1061
1062         if (selection->tracks.empty()) {
1063                 ts = &track_views;
1064         } else {
1065                 ts = &selection->tracks;
1066         }
1067
1068         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1069                 if ((*iter)->hidden()) {
1070                         continue;
1071                 }
1072                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1073         }
1074         begin_reversible_command (_("select all from punch"));
1075         selection->set (touched);
1076         commit_reversible_command ();
1077
1078 }
1079
1080 void
1081 Editor::select_all_selectables_using_loop()
1082 {
1083         Location* location = session->locations()->auto_loop_location();
1084         list<Selectable *> touched;
1085
1086         if (location == 0 || (location->end() - location->start() <= 1))  {
1087                 return;
1088         }
1089
1090
1091         TrackSelection* ts;
1092
1093         if (selection->tracks.empty()) {
1094                 ts = &track_views;
1095         } else {
1096                 ts = &selection->tracks;
1097         }
1098
1099         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1100                 if ((*iter)->hidden()) {
1101                         continue;
1102                 }
1103                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1104         }
1105         begin_reversible_command (_("select all from loop"));
1106         selection->set (touched);
1107         commit_reversible_command ();
1108
1109 }
1110
1111 void
1112 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1113 {
1114         nframes_t start;
1115         nframes_t end;
1116         list<Selectable *> touched;
1117
1118         if (after) {
1119                 begin_reversible_command (_("select all after cursor"));
1120                 start = cursor->current_frame ;
1121                 end = session->current_end_frame();
1122         } else {
1123                 if (cursor->current_frame > 0) {
1124                         begin_reversible_command (_("select all before cursor"));
1125                         start = 0;
1126                         end = cursor->current_frame - 1;
1127                 } else {
1128                         return;
1129                 }
1130         }
1131
1132
1133         TrackSelection* ts;
1134
1135         if (selection->tracks.empty()) {
1136                 ts = &track_views;
1137         } else {
1138                 ts = &selection->tracks;
1139         }
1140
1141         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1142                 if ((*iter)->hidden()) {
1143                         continue;
1144                 }
1145                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1146         }
1147         selection->set (touched);
1148         commit_reversible_command ();
1149 }
1150
1151 void
1152 Editor::select_all_selectables_using_edit (bool after)
1153 {
1154         nframes_t start;
1155         nframes_t end;
1156         list<Selectable *> touched;
1157
1158         if (after) {
1159                 begin_reversible_command (_("select all after edit"));
1160                 start = get_preferred_edit_position();
1161                 end = session->current_end_frame();
1162         } else {
1163                 if ((end = get_preferred_edit_position()) > 1) {
1164                         begin_reversible_command (_("select all before edit"));
1165                         start = 0;
1166                         end -= 1;
1167                 } else {
1168                         return;
1169                 }
1170         }
1171
1172
1173         TrackSelection* ts;
1174
1175         if (selection->tracks.empty()) {
1176                 ts = &track_views;
1177         } else {
1178                 ts = &selection->tracks;
1179         }
1180
1181         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1182                 if ((*iter)->hidden()) {
1183                         continue;
1184                 }
1185                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1186         }
1187         selection->set (touched);
1188         commit_reversible_command ();
1189 }
1190
1191 void
1192 Editor::select_all_selectables_between (bool within)
1193 {
1194         nframes64_t start;
1195         nframes64_t end;
1196         list<Selectable *> touched;
1197
1198         if (!get_edit_op_range (start, end)) {
1199                 return;
1200         }
1201
1202         TrackSelection* ts;
1203
1204         if (selection->tracks.empty()) {
1205                 ts = &track_views;
1206         } else {
1207                 ts = &selection->tracks;
1208         }
1209
1210         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1211                 if ((*iter)->hidden()) {
1212                         continue;
1213                 }
1214                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1215         }
1216
1217         selection->set (touched);
1218 }
1219
1220 void
1221 Editor::select_range_between ()
1222 {
1223         nframes64_t start;
1224         nframes64_t end;
1225         
1226         if (!get_edit_op_range (start, end)) {
1227                 return;
1228         }
1229
1230         set_mouse_mode (MouseRange);
1231         selection->set ((TimeAxisView*) 0, start, end);
1232 }
1233
1234 bool
1235 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1236 {
1237         nframes64_t m;
1238         bool ignored;
1239
1240         /* in range mode, use any existing selection */
1241
1242         if (mouse_mode == MouseRange && !selection->time.empty()) {
1243                 /* we know that these are ordered */
1244                 start = selection->time.start();
1245                 end = selection->time.end_frame();
1246                 return true;
1247         }
1248
1249         if (!mouse_frame (m, ignored)) {
1250                 /* mouse is not in a canvas, try playhead+selected marker.
1251                    this is probably most true when using menus.
1252                  */
1253
1254                 if (selection->markers.empty()) {
1255                         return false;
1256                 }
1257
1258                 start = selection->markers.front()->position();
1259                 end = session->audible_frame();
1260
1261         } else {
1262
1263                 switch (_edit_point) {
1264                 case EditAtPlayhead:
1265                         if (selection->markers.empty()) {
1266                                 /* use mouse + playhead */
1267                                 start = m;
1268                                 end = session->audible_frame();
1269                         } else {
1270                                 /* use playhead + selected marker */
1271                                 start = session->audible_frame();
1272                                 end = selection->markers.front()->position();
1273                         }
1274                         break;
1275                         
1276                 case EditAtMouse:
1277                         /* use mouse + selected marker */
1278                         if (selection->markers.empty()) {
1279                                 start = m;
1280                                 end = session->audible_frame();
1281                         } else {
1282                                 start = selection->markers.front()->position();
1283                                 end = m;
1284                         }
1285                         break;
1286                         
1287                 case EditAtSelectedMarker:
1288                         /* use mouse + selected marker */
1289                         if (selection->markers.empty()) {
1290                                 
1291                                 MessageDialog win (_("No edit range defined"),
1292                                                    false,
1293                                                    MESSAGE_INFO,
1294                                                    BUTTONS_OK);
1295
1296                                 win.set_secondary_text (
1297                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1298                                 
1299
1300                                 win.set_default_response (RESPONSE_CLOSE);
1301                                 win.set_position (Gtk::WIN_POS_MOUSE);
1302                                 win.show_all();
1303                                 
1304                                 win.run ();
1305                                 
1306                                 return false; // NO RANGE
1307                         }
1308                         start = selection->markers.front()->position();
1309                         end = m;
1310                         break;
1311                 }
1312         }
1313
1314         if (start == end) {
1315                 return false;
1316         }
1317
1318         if (start > end) {
1319                 swap (start, end);
1320         }
1321
1322         return true;
1323 }
1324
1325 void
1326 Editor::deselect_all ()
1327 {
1328         selection->clear ();
1329 }
1330
1331 Editor::ExclusiveRegionSelection::ExclusiveRegionSelection (Editor& ed, RegionView* rv)
1332         : editor (ed),
1333           regionview (rv)
1334 {
1335
1336         if (!rv || ed.current_mouse_mode() != Editing::MouseObject) {
1337                 return;
1338         }
1339         
1340         if (ed.get_selection().regions.empty() && !ed.get_selection().selected (rv)) {
1341                 ed.get_selection().set (rv, false);
1342                 remove = true;
1343         } else {
1344                 remove = false;
1345         }
1346 }
1347
1348 Editor::ExclusiveRegionSelection::~ExclusiveRegionSelection ()
1349 {
1350         if (remove && regionview) {
1351                 editor.get_selection().remove (regionview);
1352         }
1353 }
1354