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