b4532668df25df380621ab3ae0ac0712890c4124
[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         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
856                 (*i)->set_selected_regionviews (selection->regions);
857         }
858         
859         sensitize_the_right_region_actions (!selection->regions.empty());
860
861         zoomed_to_region = false;
862 }
863
864 void
865 Editor::point_selection_changed ()
866 {
867         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868                 (*i)->set_selected_points (selection->points);
869         }
870 }
871
872 void
873 Editor::select_all_in_track (Selection::Operation op)
874 {
875         list<Selectable *> touched;
876
877         if (!clicked_routeview) {
878                 return;
879         }
880         
881         clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
882
883         switch (op) {
884         case Selection::Toggle:
885                 selection->add (touched);
886                 break;
887         case Selection::Set:
888                 selection->set (touched);
889                 break;
890         case Selection::Extend:
891                 /* meaningless, because we're selecting everything */
892                 break;
893         case Selection::Add:
894                 selection->add (touched);
895                 break;
896         }
897 }
898
899 void
900 Editor::select_all (Selection::Operation op)
901 {
902         list<Selectable *> touched;
903
904         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
905                 if ((*iter)->hidden()) {
906                         continue;
907                 }
908                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
909         }
910         begin_reversible_command (_("select all"));
911         switch (op) {
912         case Selection::Add:
913                 selection->add (touched);
914                 break;
915         case Selection::Toggle:
916                 selection->add (touched);
917                 break;
918         case Selection::Set:
919                 selection->set (touched);
920                 break;
921         case Selection::Extend:
922                 /* meaningless, because we're selecting everything */
923                 break;
924         }
925         commit_reversible_command ();
926 }
927 void
928 Editor::invert_selection_in_track ()
929 {
930         list<Selectable *> touched;
931
932         if (!clicked_routeview) {
933                 return;
934         }
935         
936         clicked_routeview->get_inverted_selectables (*selection, touched);
937         selection->set (touched);
938 }
939
940 void
941 Editor::invert_selection ()
942 {
943         list<Selectable *> touched;
944         
945         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
946                 if ((*iter)->hidden()) {
947                         continue;
948                 }
949                 (*iter)->get_inverted_selectables (*selection, touched);
950         }
951
952         selection->set (touched);
953 }
954
955 bool
956 Editor::select_all_within (nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
957 {
958         list<Selectable*> touched;
959         list<Selectable*>::size_type n = 0;
960         TrackViewList touched_tracks;
961
962         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
963                 if ((*iter)->hidden()) {
964                         continue;
965                 }
966
967                 n = touched.size();
968
969                 (*iter)->get_selectables (start, end, top, bot, touched);
970                 
971                 if (n != touched.size()) {
972                         touched_tracks.push_back (*iter);
973                 }
974         }
975
976         if (touched.empty()) {
977                 return false;
978         }
979
980         if (!touched_tracks.empty()) {
981
982                 switch (op) {
983                 case Selection::Add:
984                         selection->add (touched_tracks);
985                         break;
986                 case Selection::Toggle:
987                         selection->toggle (touched_tracks);
988                         break;
989                 case Selection::Set:
990                         selection->set (touched_tracks);
991                         break;
992                 case Selection::Extend:
993                         /* not defined yet */
994                         break;
995                 }
996         }
997
998         begin_reversible_command (_("select all within"));
999         switch (op) {
1000         case Selection::Add:
1001                 selection->add (touched);
1002                 break;
1003         case Selection::Toggle:
1004                 selection->toggle (touched);
1005                 break;
1006         case Selection::Set:
1007                 selection->set (touched);
1008                 break;
1009         case Selection::Extend:
1010                 /* not defined yet */
1011                 break;
1012         }
1013         
1014         commit_reversible_command ();
1015
1016         return !touched.empty();
1017 }
1018
1019 void
1020 Editor::set_selection_from_region ()
1021 {
1022         if (selection->regions.empty()) {
1023                 return;
1024         }
1025
1026         selection->set (0, selection->regions.start(), selection->regions.end_frame());
1027         if (!Profile->get_sae()) {
1028                 set_mouse_mode (Editing::MouseRange, false);
1029         }
1030 }
1031
1032 void
1033 Editor::set_selection_from_punch()
1034 {
1035         Location* location;
1036
1037         if ((location = session->locations()->auto_punch_location()) == 0)  {
1038                 return;
1039         }
1040
1041         set_selection_from_range (*location);
1042 }
1043
1044 void
1045 Editor::set_selection_from_loop()
1046 {
1047         Location* location;
1048
1049         if ((location = session->locations()->auto_loop_location()) == 0)  {
1050                 return;
1051         }
1052         set_selection_from_range (*location);
1053 }
1054
1055 void
1056 Editor::set_selection_from_range (Location& loc)
1057 {
1058         begin_reversible_command (_("set selection from range"));
1059         selection->set (0, loc.start(), loc.end());
1060         commit_reversible_command ();
1061
1062         if (!Profile->get_sae()) {
1063                 set_mouse_mode (Editing::MouseRange, false);
1064         }
1065 }
1066
1067 void
1068 Editor::select_all_selectables_using_time_selection ()
1069 {
1070         list<Selectable *> touched;
1071
1072         if (selection->time.empty()) {
1073                 return;
1074         }
1075
1076         nframes64_t start = selection->time[clicked_selection].start;
1077         nframes64_t end = selection->time[clicked_selection].end;
1078
1079         if (end - start < 1)  {
1080                 return;
1081         }
1082
1083         TrackSelection* ts;
1084
1085         if (selection->tracks.empty()) {
1086                 ts = &track_views;
1087         } else {
1088                 ts = &selection->tracks;
1089         }
1090
1091         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1092                 if ((*iter)->hidden()) {
1093                         continue;
1094                 }
1095                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1096         }
1097
1098         begin_reversible_command (_("select all from range"));
1099         selection->set (touched);
1100         commit_reversible_command ();
1101 }
1102
1103
1104 void
1105 Editor::select_all_selectables_using_punch()
1106 {
1107         Location* location = session->locations()->auto_punch_location();
1108         list<Selectable *> touched;
1109
1110         if (location == 0 || (location->end() - location->start() <= 1))  {
1111                 return;
1112         }
1113
1114
1115         TrackSelection* ts;
1116
1117         if (selection->tracks.empty()) {
1118                 ts = &track_views;
1119         } else {
1120                 ts = &selection->tracks;
1121         }
1122
1123         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1124                 if ((*iter)->hidden()) {
1125                         continue;
1126                 }
1127                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1128         }
1129         begin_reversible_command (_("select all from punch"));
1130         selection->set (touched);
1131         commit_reversible_command ();
1132
1133 }
1134
1135 void
1136 Editor::select_all_selectables_using_loop()
1137 {
1138         Location* location = session->locations()->auto_loop_location();
1139         list<Selectable *> touched;
1140
1141         if (location == 0 || (location->end() - location->start() <= 1))  {
1142                 return;
1143         }
1144
1145
1146         TrackSelection* ts;
1147
1148         if (selection->tracks.empty()) {
1149                 ts = &track_views;
1150         } else {
1151                 ts = &selection->tracks;
1152         }
1153
1154         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1155                 if ((*iter)->hidden()) {
1156                         continue;
1157                 }
1158                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1159         }
1160         begin_reversible_command (_("select all from loop"));
1161         selection->set (touched);
1162         commit_reversible_command ();
1163
1164 }
1165
1166 void
1167 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1168 {
1169         nframes64_t start;
1170         nframes64_t end;
1171         list<Selectable *> touched;
1172
1173         if (after) {
1174                 begin_reversible_command (_("select all after cursor"));
1175                 start = cursor->current_frame ;
1176                 end = session->current_end_frame();
1177         } else {
1178                 if (cursor->current_frame > 0) {
1179                         begin_reversible_command (_("select all before cursor"));
1180                         start = 0;
1181                         end = cursor->current_frame - 1;
1182                 } else {
1183                         return;
1184                 }
1185         }
1186
1187
1188         TrackSelection* ts;
1189
1190         if (selection->tracks.empty()) {
1191                 ts = &track_views;
1192         } else {
1193                 ts = &selection->tracks;
1194         }
1195
1196         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1197                 if ((*iter)->hidden()) {
1198                         continue;
1199                 }
1200                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1201         }
1202         selection->set (touched);
1203         commit_reversible_command ();
1204 }
1205
1206 void
1207 Editor::select_all_selectables_using_edit (bool after)
1208 {
1209         nframes64_t start;
1210         nframes64_t end;
1211         list<Selectable *> touched;
1212
1213         if (after) {
1214                 begin_reversible_command (_("select all after edit"));
1215                 start = get_preferred_edit_position();
1216                 end = session->current_end_frame();
1217         } else {
1218                 if ((end = get_preferred_edit_position()) > 1) {
1219                         begin_reversible_command (_("select all before edit"));
1220                         start = 0;
1221                         end -= 1;
1222                 } else {
1223                         return;
1224                 }
1225         }
1226
1227
1228         TrackSelection* ts;
1229
1230         if (selection->tracks.empty()) {
1231                 ts = &track_views;
1232         } else {
1233                 ts = &selection->tracks;
1234         }
1235
1236         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1237                 if ((*iter)->hidden()) {
1238                         continue;
1239                 }
1240                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1241         }
1242         selection->set (touched);
1243         commit_reversible_command ();
1244 }
1245
1246 void
1247 Editor::select_all_selectables_between (bool within)
1248 {
1249         nframes64_t start;
1250         nframes64_t end;
1251         list<Selectable *> touched;
1252
1253         if (!get_edit_op_range (start, end)) {
1254                 return;
1255         }
1256
1257         TrackSelection* ts;
1258
1259         if (selection->tracks.empty()) {
1260                 ts = &track_views;
1261         } else {
1262                 ts = &selection->tracks;
1263         }
1264
1265         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1266                 if ((*iter)->hidden()) {
1267                         continue;
1268                 }
1269                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1270         }
1271
1272         selection->set (touched);
1273 }
1274
1275 void
1276 Editor::select_range_between ()
1277 {
1278         nframes64_t start;
1279         nframes64_t end;
1280         
1281         if (!get_edit_op_range (start, end)) {
1282                 return;
1283         }
1284
1285         set_mouse_mode (MouseRange);
1286         selection->set ((TimeAxisView*) 0, start, end);
1287 }
1288
1289 bool
1290 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1291 {
1292         nframes64_t m;
1293         bool ignored;
1294
1295         /* in range mode, use any existing selection */
1296
1297         if (mouse_mode == MouseRange && !selection->time.empty()) {
1298                 /* we know that these are ordered */
1299                 start = selection->time.start();
1300                 end = selection->time.end_frame();
1301                 return true;
1302         }
1303
1304         if (!mouse_frame (m, ignored)) {
1305                 /* mouse is not in a canvas, try playhead+selected marker.
1306                    this is probably most true when using menus.
1307                  */
1308
1309                 if (selection->markers.empty()) {
1310                         return false;
1311                 }
1312
1313                 start = selection->markers.front()->position();
1314                 end = session->audible_frame();
1315
1316         } else {
1317
1318                 switch (_edit_point) {
1319                 case EditAtPlayhead:
1320                         if (selection->markers.empty()) {
1321                                 /* use mouse + playhead */
1322                                 start = m;
1323                                 end = session->audible_frame();
1324                         } else {
1325                                 /* use playhead + selected marker */
1326                                 start = session->audible_frame();
1327                                 end = selection->markers.front()->position();
1328                         }
1329                         break;
1330                         
1331                 case EditAtMouse:
1332                         /* use mouse + selected marker */
1333                         if (selection->markers.empty()) {
1334                                 start = m;
1335                                 end = session->audible_frame();
1336                         } else {
1337                                 start = selection->markers.front()->position();
1338                                 end = m;
1339                         }
1340                         break;
1341                         
1342                 case EditAtSelectedMarker:
1343                         /* use mouse + selected marker */
1344                         if (selection->markers.empty()) {
1345                                 
1346                                 MessageDialog win (_("No edit range defined"),
1347                                                    false,
1348                                                    MESSAGE_INFO,
1349                                                    BUTTONS_OK);
1350
1351                                 win.set_secondary_text (
1352                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1353                                 
1354
1355                                 win.set_default_response (RESPONSE_CLOSE);
1356                                 win.set_position (Gtk::WIN_POS_MOUSE);
1357                                 win.show_all();
1358                                 
1359                                 win.run ();
1360                                 
1361                                 return false; // NO RANGE
1362                         }
1363                         start = selection->markers.front()->position();
1364                         end = m;
1365                         break;
1366                 }
1367         }
1368
1369         if (start == end) {
1370                 return false;
1371         }
1372
1373         if (start > end) {
1374                 swap (start, end);
1375         }
1376
1377         /* turn range into one delimited by start...end,
1378            not start...end-1
1379         */
1380
1381         end++;
1382
1383         return true;
1384 }
1385
1386 void
1387 Editor::deselect_all ()
1388 {
1389         selection->clear ();
1390 }
1391
1392