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