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