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