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