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