revert previous changes to region selection and action sensitivity handling
[ardour.git] / gtk2_ardour / editor_selection.cc
1 /*
2     Copyright (C) 2000-2006 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <cstdlib>
22
23 #include "pbd/stacktrace.h"
24
25 #include "ardour/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29 #include "ardour/midi_region.h"
30
31 #include "editor.h"
32 #include "actions.h"
33 #include "audio_time_axis.h"
34 #include "audio_region_view.h"
35 #include "audio_streamview.h"
36 #include "automation_line.h"
37 #include "control_point.h"
38 #include "editor_regions.h"
39 #include "editor_cursors.h"
40 #include "midi_region_view.h"
41
42 #include "i18n.h"
43
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtk;
48 using namespace Glib;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
51
52 struct TrackViewByPositionSorter
53 {
54         bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
55                 return a->y_position() < b->y_position();
56         }
57 };
58
59 bool
60 Editor::extend_selection_to_track (TimeAxisView& view)
61 {
62         if (selection->selected (&view)) {
63                 /* already selected, do nothing */
64                 return false;
65         }
66
67         if (selection->tracks.empty()) {
68
69                 if (!selection->selected (&view)) {
70                         selection->set (&view);
71                         return true;
72                 } else {
73                         return false;
74                 }
75         }
76
77         /* something is already selected, so figure out which range of things to add */
78
79         TrackViewList to_be_added;
80         TrackViewList sorted = track_views;
81         TrackViewByPositionSorter cmp;
82         bool passed_clicked = false;
83         bool forwards = true;
84
85         sorted.sort (cmp);
86
87         if (!selection->selected (&view)) {
88                 to_be_added.push_back (&view);
89         }
90
91         /* figure out if we should go forward or backwards */
92
93         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
94
95                 if ((*i) == &view) {
96                         passed_clicked = true;
97                 }
98
99                 if (selection->selected (*i)) {
100                         if (passed_clicked) {
101                                 forwards = true;
102                         } else {
103                                 forwards = false;
104                         }
105                         break;
106                 }
107         }
108
109         passed_clicked = false;
110
111         if (forwards) {
112
113                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
114
115                         if ((*i) == &view) {
116                                 passed_clicked = true;
117                                 continue;
118                         }
119
120                         if (passed_clicked) {
121                                 if ((*i)->hidden()) {
122                                         continue;
123                                 }
124                                 if (selection->selected (*i)) {
125                                         break;
126                                 } else if (!(*i)->hidden()) {
127                                         to_be_added.push_back (*i);
128                                 }
129                         }
130                 }
131
132         } else {
133
134                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
135
136                         if ((*r) == &view) {
137                                 passed_clicked = true;
138                                 continue;
139                         }
140
141                         if (passed_clicked) {
142
143                                 if ((*r)->hidden()) {
144                                         continue;
145                                 }
146
147                                 if (selection->selected (*r)) {
148                                         break;
149                                 } else if (!(*r)->hidden()) {
150                                         to_be_added.push_back (*r);
151                                 }
152                         }
153                 }
154         }
155
156         if (!to_be_added.empty()) {
157                 selection->add (to_be_added);
158                 return true;
159         }
160
161         return false;
162 }
163
164 void
165 Editor::select_all_tracks ()
166 {
167         TrackViewList visible_views;
168         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
169                 if ((*i)->marked_for_display()) {
170                         visible_views.push_back (*i);
171                 }
172         }
173         selection->set (visible_views);
174 }
175
176 /** Select clicked_axisview, unless there are no currently selected
177  *  tracks, in which case nothing will happen unless `force' is true.
178  */
179 void
180 Editor::set_selected_track_as_side_effect (Selection::Operation op, bool /*force*/)
181 {
182         if (!clicked_axisview) {
183                 return;
184         }
185
186 #if 1
187         if (!clicked_routeview) {
188                 return;
189         }
190
191         bool had_tracks = !selection->tracks.empty();
192         RouteGroup* group = clicked_routeview->route()->route_group();
193         RouteGroup& arg (_session->all_route_group());
194
195         switch (op) {
196         case Selection::Toggle:
197                 if (selection->selected (clicked_axisview)) {
198                         if (arg.is_select() && arg.is_active()) {
199                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
200                                         selection->remove(*i);
201                                 }
202                         } else if (group && group->is_active()) {
203                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
204                                         if ((*i)->route_group() == group)
205                                                 selection->remove(*i);
206                                 }
207                         } else {
208                                 selection->remove (clicked_axisview);
209                         }
210                 } else {
211                         if (arg.is_select() && arg.is_active()) {
212                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
213                                         selection->add(*i);
214                                 }
215                         } else if (group && group->is_active()) {
216                                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217                                         if ( (*i)->route_group() == group)
218                                                 selection->add(*i);
219                                 }
220                         } else {
221                                 selection->add (clicked_axisview);
222                         }
223                 }
224                 break;
225
226         case Selection::Add:
227                 if (!had_tracks && arg.is_select() && arg.is_active()) {
228                         /* nothing was selected already, and all group is active etc. so use
229                            all tracks.
230                         */
231                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
232                                 selection->add(*i);
233                         }
234                 } else if (group && group->is_active()) {
235                         for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
236                                 if ((*i)->route_group() == group)
237                                         selection->add(*i);
238                         }
239                 } else {
240                         selection->add (clicked_axisview);
241                 }
242                 break;
243
244         case Selection::Set:
245                 selection->clear();
246                 if (!had_tracks && arg.is_select() && arg.is_active()) {
247                         /* nothing was selected already, and all group is active etc. so use
248                            all tracks.
249                         */
250                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
251                                 selection->add(*i);
252                         }
253                 } else if (group && group->is_active()) {
254                         for (TrackViewList::iterator i  = track_views.begin(); i != track_views.end (); ++i) {
255                                 if ((*i)->route_group() == group)
256                                         selection->add(*i);
257                         }
258                 } else {
259                         selection->set (clicked_axisview);
260                 }
261                 break;
262
263         case Selection::Extend:
264                 selection->clear();
265                 cerr << ("Editor::set_selected_track_as_side_effect  case  Selection::Add  not yet implemented\n");
266                 break;
267         }
268
269 #else // the older version
270
271         if (!selection->tracks.empty()) {
272                 if (!selection->selected (clicked_axisview)) {
273                         selection->add (clicked_axisview);
274                 }
275
276         } else if (force) {
277                 selection->set (clicked_axisview);
278         }
279 #endif
280 }
281
282 void
283 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
284 {
285         switch (op) {
286         case Selection::Toggle:
287                 if (selection->selected (&view)) {
288                         if (!no_remove) {
289                                 selection->remove (&view);
290                         }
291                 } else {
292                         selection->add (&view);
293                 }
294                 break;
295
296         case Selection::Add:
297                 if (!selection->selected (&view)) {
298                         selection->add (&view);
299                 }
300                 break;
301
302         case Selection::Set:
303                 selection->set (&view);
304                 break;
305
306         case Selection::Extend:
307                 extend_selection_to_track (view);
308                 break;
309         }
310 }
311
312 void
313 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
314 {
315         if (!clicked_routeview) {
316                 return;
317         }
318
319         if (!press) {
320                 return;
321         }
322
323         set_selected_track (*clicked_routeview, op, no_remove);
324 }
325
326 bool
327 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
328 {
329         if (!clicked_control_point) {
330                 return false;
331         }
332
333         switch (op) {
334         case Selection::Set:
335                 selection->set (clicked_control_point);
336                 break;
337         case Selection::Add:
338                 selection->add (clicked_control_point);
339                 break;
340         case Selection::Toggle:
341                 selection->toggle (clicked_control_point);
342                 break;
343         case Selection::Extend:
344                 /* XXX */
345                 break;
346         }
347
348         return true;
349 }
350
351 void
352 Editor::get_onscreen_tracks (TrackViewList& tvl)
353 {
354         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
355                 if ((*i)->y_position() < _canvas_height) {
356                         tvl.push_back (*i);
357                 }
358         }
359 }
360
361 /** Call a slot for a given `basis' track and also for any track that is in the same
362  *  active route group with a particular set of properties.
363  *
364  *  @param sl Slot to call.
365  *  @param basis Basis track.
366  *  @param prop Properties that active edit groups must share to be included in the map.
367  */
368
369 void
370 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
371 {
372         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
373
374         if (route_basis == 0) {
375                 return;
376         }
377
378         set<RouteTimeAxisView*> tracks;
379         tracks.insert (route_basis);
380
381         RouteGroup* group = route_basis->route()->route_group();
382
383         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
384
385                 /* the basis is a member of an active route group, with the appropriate
386                    properties; find other members */
387
388                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
389                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
390                         if (v && v->route()->route_group() == group) {
391                                 tracks.insert (v);
392                         }
393                 }
394         }
395
396         /* call the slots */
397         uint32_t const sz = tracks.size ();
398
399         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
400                 sl (**i, sz);
401         }
402 }
403
404 void
405 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
406 {
407         boost::shared_ptr<Playlist> pl;
408         vector<boost::shared_ptr<Region> > results;
409         RegionView* marv;
410         boost::shared_ptr<Track> tr;
411
412         if ((tr = tv.track()) == 0) {
413                 /* bus */
414                 return;
415         }
416
417         if (&tv == &basis->get_time_axis_view()) {
418                 /* looking in same track as the original */
419                 return;
420         }
421
422         if ((pl = tr->playlist()) != 0) {
423                 pl->get_equivalent_regions (basis->region(), results);
424         }
425
426         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
427                 if ((marv = tv.view()->find_view (*ir)) != 0) {
428                         all_equivs->push_back (marv);
429                 }
430         }
431 }
432
433 void
434 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
435 {
436         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
437
438         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
439
440         equivalent_regions.push_back (basis);
441 }
442
443 RegionSelection
444 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
445 {
446         RegionSelection equivalent;
447
448         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
449
450                 vector<RegionView*> eq;
451
452                 mapover_tracks (
453                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
454                         &(*i)->get_time_axis_view(), prop);
455
456                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
457                         equivalent.add (*j);
458                 }
459
460                 equivalent.add (*i);
461         }
462
463         return equivalent;
464 }
465
466
467 int
468 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
469 {
470         int region_count = 0;
471
472         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
473
474                 RouteTimeAxisView* tatv;
475
476                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
477
478                         boost::shared_ptr<Playlist> pl;
479                         vector<boost::shared_ptr<Region> > results;
480                         RegionView* marv;
481                         boost::shared_ptr<Track> tr;
482
483                         if ((tr = tatv->track()) == 0) {
484                                 /* bus */
485                                 continue;
486                         }
487
488                         if ((pl = (tr->playlist())) != 0) {
489                                 pl->get_region_list_equivalent_regions (region, results);
490                         }
491
492                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
493                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
494                                         region_count++;
495                                 }
496                         }
497
498                 }
499         }
500
501         return region_count;
502 }
503
504
505 bool
506 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
507 {
508         vector<RegionView*> all_equivalent_regions;
509         bool commit = false;
510
511         if (!clicked_regionview || !clicked_routeview) {
512                 return false;
513         }
514
515         if (press) {
516                 button_release_can_deselect = false;
517         }
518
519         if (op == Selection::Toggle || op == Selection::Set) {
520
521
522                 switch (op) {
523                 case Selection::Toggle:
524                         if (selection->selected (clicked_regionview)) {
525                                 if (press) {
526
527                                         /* whatever was clicked was selected already; do nothing here but allow
528                                            the button release to deselect it
529                                         */
530
531                                         button_release_can_deselect = true;
532
533                                 } else {
534                                         if (button_release_can_deselect) {
535
536                                                 /* just remove this one region, but only on a permitted button release */
537
538                                                 selection->remove (clicked_regionview);
539                                                 commit = true;
540
541                                                 /* no more deselect action on button release till a new press
542                                                    finds an already selected object.
543                                                 */
544
545                                                 button_release_can_deselect = false;
546                                         }
547                                 }
548
549                         } else {
550
551                                 if (press) {
552
553                                         if (selection->selected (clicked_routeview)) {
554                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
555                                         } else {
556                                                 all_equivalent_regions.push_back (clicked_regionview);
557                                         }
558
559                                         /* add all the equivalent regions, but only on button press */
560
561                                         if (!all_equivalent_regions.empty()) {
562                                                 commit = true;
563                                         }
564
565                                         selection->add (all_equivalent_regions);
566                                 }
567                         }
568                         break;
569
570                 case Selection::Set:
571                         if (!selection->selected (clicked_regionview)) {
572                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
573                                 selection->set (all_equivalent_regions);
574                                 commit = true;
575                         } else {
576                                 /* no commit necessary: clicked on an already selected region */
577                                 goto out;
578                         }
579                         break;
580
581                 default:
582                         /* silly compiler */
583                         break;
584                 }
585
586         } else if (op == Selection::Extend) {
587
588                 list<Selectable*> results;
589                 framepos_t last_frame;
590                 framepos_t first_frame;
591                 bool same_track = false;
592
593                 /* 1. find the last selected regionview in the track that was clicked in */
594
595                 last_frame = 0;
596                 first_frame = max_framepos;
597
598                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
599                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
600
601                                 if ((*x)->region()->last_frame() > last_frame) {
602                                         last_frame = (*x)->region()->last_frame();
603                                 }
604
605                                 if ((*x)->region()->first_frame() < first_frame) {
606                                         first_frame = (*x)->region()->first_frame();
607                                 }
608
609                                 same_track = true;
610                         }
611                 }
612
613                 if (same_track) {
614
615                         /* 2. figure out the boundaries for our search for new objects */
616
617                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
618                         case OverlapNone:
619                                 if (last_frame < clicked_regionview->region()->first_frame()) {
620                                         first_frame = last_frame;
621                                         last_frame = clicked_regionview->region()->last_frame();
622                                 } else {
623                                         last_frame = first_frame;
624                                         first_frame = clicked_regionview->region()->first_frame();
625                                 }
626                                 break;
627
628                         case OverlapExternal:
629                                 if (last_frame < clicked_regionview->region()->first_frame()) {
630                                         first_frame = last_frame;
631                                         last_frame = clicked_regionview->region()->last_frame();
632                                 } else {
633                                         last_frame = first_frame;
634                                         first_frame = clicked_regionview->region()->first_frame();
635                                 }
636                                 break;
637
638                         case OverlapInternal:
639                                 if (last_frame < clicked_regionview->region()->first_frame()) {
640                                         first_frame = last_frame;
641                                         last_frame = clicked_regionview->region()->last_frame();
642                                 } else {
643                                         last_frame = first_frame;
644                                         first_frame = clicked_regionview->region()->first_frame();
645                                 }
646                                 break;
647
648                         case OverlapStart:
649                         case OverlapEnd:
650                                 /* nothing to do except add clicked region to selection, since it
651                                    overlaps with the existing selection in this track.
652                                 */
653                                 break;
654                         }
655
656                 } else {
657
658                         /* click in a track that has no regions selected, so extend vertically
659                            to pick out all regions that are defined by the existing selection
660                            plus this one.
661                         */
662
663
664                         first_frame = clicked_regionview->region()->position();
665                         last_frame = clicked_regionview->region()->last_frame();
666
667                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
668                                 if ((*i)->region()->position() < first_frame) {
669                                         first_frame = (*i)->region()->position();
670                                 }
671                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
672                                         last_frame = (*i)->region()->last_frame();
673                                 }
674                         }
675                 }
676
677                 /* 2. find all the tracks we should select in */
678
679                 set<RouteTimeAxisView*> relevant_tracks;
680
681                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
682                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
683                         if (r) {
684                                 relevant_tracks.insert (r);
685                         }
686                 }
687
688                 set<RouteTimeAxisView*> already_in_selection;
689
690                 if (relevant_tracks.empty()) {
691
692                         /* no tracks selected .. thus .. if the
693                            regionview we're in isn't selected
694                            (i.e. we're about to extend to it), then
695                            find all tracks between the this one and
696                            any selected ones.
697                         */
698
699                         if (!selection->selected (clicked_regionview)) {
700
701                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
702
703                                 if (rtv) {
704
705                                         /* add this track to the ones we will search */
706
707                                         relevant_tracks.insert (rtv);
708
709                                         /* find the track closest to this one that
710                                            already a selected region.
711                                         */
712
713                                         RouteTimeAxisView* closest = 0;
714                                         int distance = INT_MAX;
715                                         int key = rtv->route()->order_key ("editor");
716
717                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
718
719                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
720
721                                                 if (artv && artv != rtv) {
722
723                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
724
725                                                         result = already_in_selection.insert (artv);
726
727                                                         if (result.second) {
728                                                                 /* newly added to already_in_selection */
729
730                                                                 int d = artv->route()->order_key ("editor");
731
732                                                                 d -= key;
733
734                                                                 if (abs (d) < distance) {
735                                                                         distance = abs (d);
736                                                                         closest = artv;
737                                                                 }
738                                                         }
739                                                 }
740                                         }
741
742                                         if (closest) {
743
744                                                 /* now add all tracks between that one and this one */
745
746                                                 int okey = closest->route()->order_key ("editor");
747
748                                                 if (okey > key) {
749                                                         swap (okey, key);
750                                                 }
751
752                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
753                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
754                                                         if (artv && artv != rtv) {
755
756                                                                 int k = artv->route()->order_key ("editor");
757
758                                                                 if (k >= okey && k <= key) {
759
760                                                                         /* in range but don't add it if
761                                                                            it already has tracks selected.
762                                                                            this avoids odd selection
763                                                                            behaviour that feels wrong.
764                                                                         */
765
766                                                                         if (find (already_in_selection.begin(),
767                                                                                   already_in_selection.end(),
768                                                                                   artv) == already_in_selection.end()) {
769
770                                                                                 relevant_tracks.insert (artv);
771                                                                         }
772                                                                 }
773                                                         }
774                                                 }
775                                         }
776                                 }
777                         }
778                 }
779
780                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
781                    one that was clicked.
782                 */
783
784                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
785                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
786                 }
787
788                 /* 4. convert to a vector of regions */
789
790                 vector<RegionView*> regions;
791
792                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
793                         RegionView* arv;
794
795                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
796                                 regions.push_back (arv);
797                         }
798                 }
799
800                 if (!regions.empty()) {
801                         selection->add (regions);
802                         commit = true;
803                 }
804         }
805
806 out:
807         return commit;
808 }
809
810
811 void
812 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
813 {
814         vector<RegionView*> all_equivalent_regions;
815
816         get_regions_corresponding_to (region, all_equivalent_regions);
817
818         if (all_equivalent_regions.empty()) {
819                 return;
820         }
821
822         begin_reversible_command (_("set selected regions"));
823
824         switch (op) {
825         case Selection::Toggle:
826                 /* XXX this is not correct */
827                 selection->toggle (all_equivalent_regions);
828                 break;
829         case Selection::Set:
830                 selection->set (all_equivalent_regions);
831                 break;
832         case Selection::Extend:
833                 selection->add (all_equivalent_regions);
834                 break;
835         case Selection::Add:
836                 selection->add (all_equivalent_regions);
837                 break;
838         }
839
840         commit_reversible_command () ;
841 }
842
843 bool
844 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
845 {
846         RegionView* rv;
847         boost::shared_ptr<Region> r (weak_r.lock());
848
849         if (!r) {
850                 return true;
851         }
852
853         if ((rv = sv->find_view (r)) == 0) {
854                 return true;
855         }
856
857         /* don't reset the selection if its something other than
858            a single other region.
859         */
860
861         if (selection->regions.size() > 1) {
862                 return true;
863         }
864
865         begin_reversible_command (_("set selected regions"));
866
867         selection->set (rv);
868
869         commit_reversible_command () ;
870
871         return true;
872 }
873
874 void
875 Editor::track_selection_changed ()
876 {
877         switch (selection->tracks.size()) {
878         case 0:
879                 break;
880         default:
881                 set_selected_mixer_strip (*(selection->tracks.front()));
882                 break;
883         }
884
885         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
886
887                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
888
889                 (*i)->set_selected (yn);
890
891                 TimeAxisView::Children c = (*i)->get_child_list ();
892                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
893                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
894                 }
895
896                 if (yn &&
897                     ((mouse_mode == MouseRange) ||
898                      ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
899                         (*i)->reshow_selection (selection->time);
900                 } else {
901                         (*i)->hide_selection ();
902                 }
903         }
904
905         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
906 }
907
908 void
909 Editor::time_selection_changed ()
910 {
911         if (Profile->get_sae()) {
912                 return;
913         }
914
915         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
916                 (*i)->hide_selection ();
917         }
918
919         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
920                 (*i)->show_selection (selection->time);
921         }
922
923         if (selection->time.empty()) {
924                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
925         } else {
926                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
927         }
928 }
929
930 /** Set all region actions to have a given sensitivity */
931 void
932 Editor::sensitize_all_region_actions (bool s)
933 {
934         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
935
936         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
937                 (*i)->set_sensitive (s);
938         }
939
940         _all_region_actions_sensitized = s;
941 }
942
943 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
944  *  This method should be called just before displaying a Region menu.  When a Region menu is not
945  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
946  *  on entered_regionviews work without having to check sensitivity every time the selection or
947  *  entered_regionview changes.
948  *
949  *  This method also sets up toggle action state as appropriate.
950  */
951 void
952 Editor::sensitize_the_right_region_actions ()
953 {
954         if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
955                 sensitize_all_region_actions (false);
956                 if (!selection->time.empty()) {
957                         _region_actions->get_action("split-region")->set_sensitive (true);
958                 }
959                 return;
960
961         } else if (mouse_mode != MouseObject) {
962                 sensitize_all_region_actions (false);
963                 return;
964         }
965
966         /* We get here if we are in Object mode */
967
968         RegionSelection rs = get_regions_from_selection_and_entered ();
969         sensitize_all_region_actions (!rs.empty ());
970
971         _ignore_region_action = true;
972
973         /* Look through the regions that are selected and make notes about what we have got */
974
975         bool have_audio = false;
976         bool have_midi = false;
977         bool have_locked = false;
978         bool have_unlocked = false;
979         bool have_position_lock_style_audio = false;
980         bool have_position_lock_style_music = false;
981         bool have_muted = false;
982         bool have_unmuted = false;
983         bool have_opaque = false;
984         bool have_non_opaque = false;
985         bool have_not_at_natural_position = false;
986         bool have_envelope_visible = false;
987         bool have_envelope_invisible = false;
988         bool have_envelope_active = false;
989         bool have_envelope_inactive = false;
990         bool have_non_unity_scale_amplitude = false;
991         bool have_compound_regions = false;
992
993         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
994
995                 boost::shared_ptr<Region> r = (*i)->region ();
996                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
997
998                 if (ar) {
999                         have_audio = true;
1000                 }
1001
1002                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1003                         have_midi = true;
1004                 }
1005
1006                 if (r->is_compound()) {
1007                         have_compound_regions = true;
1008                 }
1009
1010                 if (r->locked()) {
1011                         have_locked = true;
1012                 } else {
1013                         have_unlocked = true;
1014                 }
1015
1016                 if (r->position_lock_style() == MusicTime) {
1017                         have_position_lock_style_music = true;
1018                 } else {
1019                         have_position_lock_style_audio = true;
1020                 }
1021
1022                 if (r->muted()) {
1023                         have_muted = true;
1024                 } else {
1025                         have_unmuted = true;
1026                 }
1027
1028                 if (r->opaque()) {
1029                         have_opaque = true;
1030                 } else {
1031                         have_non_opaque = true;
1032                 }
1033
1034                 if (!r->at_natural_position()) {
1035                         have_not_at_natural_position = true;
1036                 }
1037
1038                 if (ar) {
1039                         /* its a bit unfortunate that "envelope visible" is a view-only
1040                            property. we have to find the regionview to able to check
1041                            its current setting.
1042                         */
1043
1044                         have_envelope_invisible = true;
1045
1046                         if (*i) {
1047                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1048                                 if (arv && arv->envelope_visible()) {
1049                                         have_envelope_visible = true;
1050                                 }
1051                         }
1052
1053                         if (ar->envelope_active()) {
1054                                 have_envelope_active = true;
1055                         } else {
1056                                 have_envelope_inactive = true;
1057                         }
1058
1059                         if (ar->scale_amplitude() != 1) {
1060                                 have_non_unity_scale_amplitude = true;
1061                         }
1062                 }
1063         }
1064
1065         if (rs.size() > 1) {
1066                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1067                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1068                 _region_actions->get_action("rename-region")->set_sensitive (false);
1069                 if (have_audio) {
1070                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1071                 } else {
1072                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1073                 }
1074         } else if (rs.size() == 1) {
1075                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1076                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1077                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1078         }
1079
1080         if (!have_midi) {
1081                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1082                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1083                 _region_actions->get_action("fork-region")->set_sensitive (false);
1084                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1085         }
1086
1087         if (_edit_point == EditAtMouse) {
1088                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1089                 _region_actions->get_action("trim-front")->set_sensitive (false);
1090                 _region_actions->get_action("trim-back")->set_sensitive (false);
1091                 _region_actions->get_action("split-region")->set_sensitive (false);
1092                 _region_actions->get_action("place-transient")->set_sensitive (false);
1093         }
1094
1095         if (have_compound_regions) {
1096                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1097         } else {
1098                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1099         }
1100
1101         if (have_audio) {
1102
1103                 if (have_envelope_visible && !have_envelope_invisible) {
1104                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1105                 } else if (have_envelope_visible && have_envelope_invisible) {
1106                         // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1107                 }
1108
1109                 if (have_envelope_active && !have_envelope_inactive) {
1110                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1111                 } else if (have_envelope_active && have_envelope_inactive) {
1112                         // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1113                 }
1114
1115         } else {
1116
1117                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1118                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1119                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1120                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1121                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1122
1123         }
1124
1125         if (!have_non_unity_scale_amplitude || !have_audio) {
1126                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1127         }
1128
1129         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1130         if (have_locked && have_unlocked) {
1131                 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1132         }
1133
1134         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1135
1136         if (have_position_lock_style_music && have_position_lock_style_audio) {
1137                 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1138         }
1139
1140         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1141         if (have_muted && have_unmuted) {
1142                 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1143         }
1144
1145         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1146         if (have_opaque && have_non_opaque) {
1147                 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1148         }
1149
1150         if (!have_not_at_natural_position) {
1151                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1152         }
1153
1154         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1155         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1156                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1157         } else {
1158                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1159         }
1160
1161         _ignore_region_action = false;
1162
1163         _all_region_actions_sensitized = false;
1164 }
1165
1166
1167 void
1168 Editor::region_selection_changed ()
1169 {
1170         _regions->block_change_connection (true);
1171         editor_regions_selection_changed_connection.block(true);
1172
1173         if (_region_selection_change_updates_region_list) {
1174                 _regions->unselect_all ();
1175         }
1176
1177         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1178                 (*i)->set_selected_regionviews (selection->regions);
1179         }
1180
1181         if (_region_selection_change_updates_region_list) {
1182                 _regions->set_selected (selection->regions);
1183         }
1184
1185         _regions->block_change_connection (false);
1186         editor_regions_selection_changed_connection.block(false);
1187
1188         if (!_all_region_actions_sensitized) {
1189                 /* This selection change might have changed what region actions
1190                    are allowed, so sensitize them all in case a key is pressed.
1191                 */
1192                 sensitize_all_region_actions (true);
1193         }
1194 }
1195
1196 void
1197 Editor::point_selection_changed ()
1198 {
1199         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1200                 (*i)->set_selected_points (selection->points);
1201         }
1202 }
1203
1204 void
1205 Editor::select_all_in_track (Selection::Operation op)
1206 {
1207         list<Selectable *> touched;
1208
1209         if (!clicked_routeview) {
1210                 return;
1211         }
1212
1213         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1214
1215         switch (op) {
1216         case Selection::Toggle:
1217                 selection->add (touched);
1218                 break;
1219         case Selection::Set:
1220                 selection->set (touched);
1221                 break;
1222         case Selection::Extend:
1223                 /* meaningless, because we're selecting everything */
1224                 break;
1225         case Selection::Add:
1226                 selection->add (touched);
1227                 break;
1228         }
1229 }
1230
1231 void
1232 Editor::select_all_internal_edit (Selection::Operation)
1233 {
1234         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1235                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1236                 if (mrv) {
1237                         mrv->select_all_notes ();
1238                 }
1239         }
1240 }
1241
1242 void
1243 Editor::select_all (Selection::Operation op)
1244 {
1245         list<Selectable *> touched;
1246
1247         if (_internal_editing) {
1248                 select_all_internal_edit (op);
1249                 return;
1250         }
1251
1252         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1253                 if ((*iter)->hidden()) {
1254                         continue;
1255                 }
1256                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1257         }
1258         begin_reversible_command (_("select all"));
1259         switch (op) {
1260         case Selection::Add:
1261                 selection->add (touched);
1262                 break;
1263         case Selection::Toggle:
1264                 selection->add (touched);
1265                 break;
1266         case Selection::Set:
1267                 selection->set (touched);
1268                 break;
1269         case Selection::Extend:
1270                 /* meaningless, because we're selecting everything */
1271                 break;
1272         }
1273         commit_reversible_command ();
1274 }
1275
1276 void
1277 Editor::invert_selection_in_track ()
1278 {
1279         list<Selectable *> touched;
1280
1281         if (!clicked_routeview) {
1282                 return;
1283         }
1284
1285         clicked_routeview->get_inverted_selectables (*selection, touched);
1286         selection->set (touched);
1287 }
1288
1289 void
1290 Editor::invert_selection ()
1291 {
1292         list<Selectable *> touched;
1293
1294         if (_internal_editing) {
1295                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1296                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1297                         if (mrv) {
1298                                 mrv->invert_selection ();
1299                         }
1300                 }
1301                 return;
1302         }
1303
1304         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1305                 if ((*iter)->hidden()) {
1306                         continue;
1307                 }
1308                 (*iter)->get_inverted_selectables (*selection, touched);
1309         }
1310
1311         selection->set (touched);
1312 }
1313
1314 /** @param start Start time in session frames.
1315  *  @param end End time in session frames.
1316  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1317  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1318  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1319  *  within the region are already selected.
1320  */
1321 void
1322 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1323 {
1324         list<Selectable*> found;
1325
1326         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1327
1328                 if ((*iter)->hidden()) {
1329                         continue;
1330                 }
1331
1332                 (*iter)->get_selectables (start, end, top, bot, found);
1333         }
1334
1335         if (found.empty()) {
1336                 return;
1337         }
1338
1339         if (preserve_if_selected && op != Selection::Toggle) {
1340                 list<Selectable*>::iterator i = found.begin();
1341                 while (i != found.end() && (*i)->get_selected()) {
1342                         ++i;
1343                 }
1344
1345                 if (i == found.end()) {
1346                         return;
1347                 }
1348         }
1349
1350         begin_reversible_command (_("select all within"));
1351         switch (op) {
1352         case Selection::Add:
1353                 selection->add (found);
1354                 break;
1355         case Selection::Toggle:
1356                 selection->toggle (found);
1357                 break;
1358         case Selection::Set:
1359                 selection->set (found);
1360                 break;
1361         case Selection::Extend:
1362                 /* not defined yet */
1363                 break;
1364         }
1365
1366         commit_reversible_command ();
1367 }
1368
1369 void
1370 Editor::set_selection_from_region ()
1371 {
1372         if (selection->regions.empty()) {
1373                 return;
1374         }
1375
1376         selection->set (selection->regions.start(), selection->regions.end_frame());
1377         if (!Profile->get_sae()) {
1378                 set_mouse_mode (Editing::MouseRange, false);
1379         }
1380 }
1381
1382 void
1383 Editor::set_selection_from_punch()
1384 {
1385         Location* location;
1386
1387         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1388                 return;
1389         }
1390
1391         set_selection_from_range (*location);
1392 }
1393
1394 void
1395 Editor::set_selection_from_loop()
1396 {
1397         Location* location;
1398
1399         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1400                 return;
1401         }
1402         set_selection_from_range (*location);
1403 }
1404
1405 void
1406 Editor::set_selection_from_range (Location& loc)
1407 {
1408         begin_reversible_command (_("set selection from range"));
1409         selection->set (loc.start(), loc.end());
1410         commit_reversible_command ();
1411
1412         if (!Profile->get_sae()) {
1413                 set_mouse_mode (Editing::MouseRange, false);
1414         }
1415 }
1416
1417 void
1418 Editor::select_all_selectables_using_time_selection ()
1419 {
1420         list<Selectable *> touched;
1421
1422         if (selection->time.empty()) {
1423                 return;
1424         }
1425
1426         framepos_t start = selection->time[clicked_selection].start;
1427         framepos_t end = selection->time[clicked_selection].end;
1428
1429         if (end - start < 1)  {
1430                 return;
1431         }
1432
1433         TrackViewList* ts;
1434
1435         if (selection->tracks.empty()) {
1436                 ts = &track_views;
1437         } else {
1438                 ts = &selection->tracks;
1439         }
1440
1441         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1442                 if ((*iter)->hidden()) {
1443                         continue;
1444                 }
1445                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1446         }
1447
1448         begin_reversible_command (_("select all from range"));
1449         selection->set (touched);
1450         commit_reversible_command ();
1451 }
1452
1453
1454 void
1455 Editor::select_all_selectables_using_punch()
1456 {
1457         Location* location = _session->locations()->auto_punch_location();
1458         list<Selectable *> touched;
1459
1460         if (location == 0 || (location->end() - location->start() <= 1))  {
1461                 return;
1462         }
1463
1464
1465         TrackViewList* ts;
1466
1467         if (selection->tracks.empty()) {
1468                 ts = &track_views;
1469         } else {
1470                 ts = &selection->tracks;
1471         }
1472
1473         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1474                 if ((*iter)->hidden()) {
1475                         continue;
1476                 }
1477                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1478         }
1479         begin_reversible_command (_("select all from punch"));
1480         selection->set (touched);
1481         commit_reversible_command ();
1482
1483 }
1484
1485 void
1486 Editor::select_all_selectables_using_loop()
1487 {
1488         Location* location = _session->locations()->auto_loop_location();
1489         list<Selectable *> touched;
1490
1491         if (location == 0 || (location->end() - location->start() <= 1))  {
1492                 return;
1493         }
1494
1495
1496         TrackViewList* ts;
1497
1498         if (selection->tracks.empty()) {
1499                 ts = &track_views;
1500         } else {
1501                 ts = &selection->tracks;
1502         }
1503
1504         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1505                 if ((*iter)->hidden()) {
1506                         continue;
1507                 }
1508                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1509         }
1510         begin_reversible_command (_("select all from loop"));
1511         selection->set (touched);
1512         commit_reversible_command ();
1513
1514 }
1515
1516 void
1517 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1518 {
1519         framepos_t start;
1520         framepos_t end;
1521         list<Selectable *> touched;
1522
1523         if (after) {
1524                 start = cursor->current_frame;
1525                 end = _session->current_end_frame();
1526         } else {
1527                 if (cursor->current_frame > 0) {
1528                         start = 0;
1529                         end = cursor->current_frame - 1;
1530                 } else {
1531                         return;
1532                 }
1533         }
1534
1535         if (_internal_editing) {
1536                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1537                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1538                         if (mrv) {
1539                                 mrv->select_range (start, end);
1540                         }
1541                 }
1542                 return;
1543         }
1544
1545         if (after) {
1546                 begin_reversible_command (_("select all after cursor"));
1547         } else {
1548                 begin_reversible_command (_("select all before cursor"));
1549         }
1550
1551         TrackViewList* ts;
1552
1553         if (selection->tracks.empty()) {
1554                 ts = &track_views;
1555         } else {
1556                 ts = &selection->tracks;
1557         }
1558
1559         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1560                 if ((*iter)->hidden()) {
1561                         continue;
1562                 }
1563                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1564         }
1565         selection->set (touched);
1566         commit_reversible_command ();
1567 }
1568
1569 void
1570 Editor::select_all_selectables_using_edit (bool after)
1571 {
1572         framepos_t start;
1573         framepos_t end;
1574         list<Selectable *> touched;
1575
1576         if (after) {
1577                 start = get_preferred_edit_position();
1578                 end = _session->current_end_frame();
1579         } else {
1580                 if ((end = get_preferred_edit_position()) > 1) {
1581                         start = 0;
1582                         end -= 1;
1583                 } else {
1584                         return;
1585                 }
1586         }
1587
1588         if (_internal_editing) {
1589                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1590                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1591                         mrv->select_range (start, end);
1592                 }
1593                 return;
1594         }
1595
1596         if (after) {
1597                 begin_reversible_command (_("select all after edit"));
1598         } else {
1599                 begin_reversible_command (_("select all before edit"));
1600         }
1601
1602         TrackViewList* ts;
1603
1604         if (selection->tracks.empty()) {
1605                 ts = &track_views;
1606         } else {
1607                 ts = &selection->tracks;
1608         }
1609
1610         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1611                 if ((*iter)->hidden()) {
1612                         continue;
1613                 }
1614                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1615         }
1616         selection->set (touched);
1617         commit_reversible_command ();
1618 }
1619
1620 void
1621 Editor::select_all_selectables_between (bool /*within*/)
1622 {
1623         framepos_t start;
1624         framepos_t end;
1625         list<Selectable *> touched;
1626
1627         if (!get_edit_op_range (start, end)) {
1628                 return;
1629         }
1630
1631         if (_internal_editing) {
1632                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1633                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1634                         mrv->select_range (start, end);
1635                 }
1636                 return;
1637         }
1638
1639         TrackViewList* ts;
1640
1641         if (selection->tracks.empty()) {
1642                 ts = &track_views;
1643         } else {
1644                 ts = &selection->tracks;
1645         }
1646
1647         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1648                 if ((*iter)->hidden()) {
1649                         continue;
1650                 }
1651                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1652         }
1653
1654         selection->set (touched);
1655 }
1656
1657 void
1658 Editor::select_range_between ()
1659 {
1660         framepos_t start;
1661         framepos_t end;
1662
1663         if (mouse_mode == MouseRange && !selection->time.empty()) {
1664                 selection->clear_time ();
1665         }
1666
1667         if (!get_edit_op_range (start, end)) {
1668                 return;
1669         }
1670
1671         set_mouse_mode (MouseRange);
1672         selection->set (start, end);
1673 }
1674
1675 bool
1676 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1677 {
1678         framepos_t m;
1679         bool ignored;
1680
1681         /* in range mode, use any existing selection */
1682
1683         if (mouse_mode == MouseRange && !selection->time.empty()) {
1684                 /* we know that these are ordered */
1685                 start = selection->time.start();
1686                 end = selection->time.end_frame();
1687                 return true;
1688         }
1689
1690         if (!mouse_frame (m, ignored)) {
1691                 /* mouse is not in a canvas, try playhead+selected marker.
1692                    this is probably most true when using menus.
1693                 */
1694
1695                 if (selection->markers.empty()) {
1696                         return false;
1697                 }
1698
1699                 start = selection->markers.front()->position();
1700                 end = _session->audible_frame();
1701
1702         } else {
1703
1704                 switch (_edit_point) {
1705                 case EditAtPlayhead:
1706                         if (selection->markers.empty()) {
1707                                 /* use mouse + playhead */
1708                                 start = m;
1709                                 end = _session->audible_frame();
1710                         } else {
1711                                 /* use playhead + selected marker */
1712                                 start = _session->audible_frame();
1713                                 end = selection->markers.front()->position();
1714                         }
1715                         break;
1716
1717                 case EditAtMouse:
1718                         /* use mouse + selected marker */
1719                         if (selection->markers.empty()) {
1720                                 start = m;
1721                                 end = _session->audible_frame();
1722                         } else {
1723                                 start = selection->markers.front()->position();
1724                                 end = m;
1725                         }
1726                         break;
1727
1728                 case EditAtSelectedMarker:
1729                         /* use mouse + selected marker */
1730                         if (selection->markers.empty()) {
1731
1732                                 MessageDialog win (_("No edit range defined"),
1733                                                    false,
1734                                                    MESSAGE_INFO,
1735                                                    BUTTONS_OK);
1736
1737                                 win.set_secondary_text (
1738                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1739
1740
1741                                 win.set_default_response (RESPONSE_CLOSE);
1742                                 win.set_position (Gtk::WIN_POS_MOUSE);
1743                                 win.show_all();
1744
1745                                 win.run ();
1746
1747                                 return false; // NO RANGE
1748                         }
1749                         start = selection->markers.front()->position();
1750                         end = m;
1751                         break;
1752                 }
1753         }
1754
1755         if (start == end) {
1756                 return false;
1757         }
1758
1759         if (start > end) {
1760                 swap (start, end);
1761         }
1762
1763         /* turn range into one delimited by start...end,
1764            not start...end-1
1765         */
1766
1767         end++;
1768
1769         return true;
1770 }
1771
1772 void
1773 Editor::deselect_all ()
1774 {
1775         selection->clear ();
1776 }
1777
1778 long
1779 Editor::select_range_around_region (RegionView* rv)
1780 {
1781         assert (rv);
1782
1783         selection->set (&rv->get_time_axis_view());
1784
1785         selection->time.clear ();
1786         boost::shared_ptr<Region> r = rv->region ();
1787         return selection->set (r->position(), r->position() + r->length());
1788 }