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