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