tweak up the region context menu quite a bit
[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 /** Call a slot for a given `basis' track and also for any track that is in the same
405  *  active route group with a particular set of properties.
406  *
407  *  @param sl Slot to call.
408  *  @param basis Basis track.
409  *  @param prop Properties that active edit groups must share to be included in the map.
410  */
411
412 void
413 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
414 {
415         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
416         set<boost::shared_ptr<Playlist> > playlists;
417
418         if (route_basis == 0) {
419                 return;
420         }
421
422         set<RouteTimeAxisView*> tracks;
423         tracks.insert (route_basis);
424
425         RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
426
427         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
428
429                 /* the basis is a member of an active route group, with the appropriate
430                    properties; find other members */
431
432                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
433                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
434
435                         if (v && v->route()->route_group() == group) {
436                                 
437                                 boost::shared_ptr<Track> t = v->track();
438                                 if (t) {
439                                         if (playlists.insert (t->playlist()).second) {
440                                                 /* haven't seen this playlist yet */
441                                                 tracks.insert (v);
442                                         }
443                                 } else {
444                                         /* not actually a "Track", but a timeaxis view that
445                                            we should mapover anyway.
446                                         */
447                                         tracks.insert (v);
448                                 }
449                         }
450                 }
451         }
452
453         /* call the slots */
454         uint32_t const sz = tracks.size ();
455
456         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
457                 sl (**i, sz);
458         }
459 }
460
461 void
462 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
463 {
464         boost::shared_ptr<Playlist> pl;
465         vector<boost::shared_ptr<Region> > results;
466         RegionView* marv;
467         boost::shared_ptr<Track> tr;
468
469         if ((tr = tv.track()) == 0) {
470                 /* bus */
471                 return;
472         }
473
474         if (&tv == &basis->get_time_axis_view()) {
475                 /* looking in same track as the original */
476                 return;
477         }
478
479         if ((pl = tr->playlist()) != 0) {
480                 pl->get_equivalent_regions (basis->region(), results);
481         }
482
483         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
484                 if ((marv = tv.view()->find_view (*ir)) != 0) {
485                         all_equivs->push_back (marv);
486                 }
487         }
488 }
489
490 void
491 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
492 {
493         mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
494
495         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
496
497         equivalent_regions.push_back (basis);
498 }
499
500 RegionSelection
501 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
502 {
503         RegionSelection equivalent;
504
505         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
506
507                 vector<RegionView*> eq;
508
509                 mapover_tracks_with_unique_playlists (
510                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
511                         &(*i)->get_time_axis_view(), prop);
512
513                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
514                         equivalent.add (*j);
515                 }
516
517                 equivalent.add (*i);
518         }
519
520         return equivalent;
521 }
522
523
524 int
525 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
526 {
527         int region_count = 0;
528
529         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
530
531                 RouteTimeAxisView* tatv;
532
533                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
534
535                         boost::shared_ptr<Playlist> pl;
536                         vector<boost::shared_ptr<Region> > results;
537                         RegionView* marv;
538                         boost::shared_ptr<Track> tr;
539
540                         if ((tr = tatv->track()) == 0) {
541                                 /* bus */
542                                 continue;
543                         }
544
545                         if ((pl = (tr->playlist())) != 0) {
546                                 pl->get_region_list_equivalent_regions (region, results);
547                         }
548
549                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
550                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
551                                         region_count++;
552                                 }
553                         }
554
555                 }
556         }
557
558         return region_count;
559 }
560
561
562 bool
563 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
564 {
565         vector<RegionView*> all_equivalent_regions;
566         bool commit = false;
567
568         if (!clicked_regionview || !clicked_routeview) {
569                 return false;
570         }
571
572         if (press) {
573                 button_release_can_deselect = false;
574         }
575
576         if (op == Selection::Toggle || op == Selection::Set) {
577
578
579                 switch (op) {
580                 case Selection::Toggle:
581                         if (selection->selected (clicked_regionview)) {
582                                 if (press) {
583
584                                         /* whatever was clicked was selected already; do nothing here but allow
585                                            the button release to deselect it
586                                         */
587
588                                         button_release_can_deselect = true;
589
590                                 } else {
591                                         if (button_release_can_deselect) {
592
593                                                 /* just remove this one region, but only on a permitted button release */
594
595                                                 selection->remove (clicked_regionview);
596                                                 commit = true;
597
598                                                 /* no more deselect action on button release till a new press
599                                                    finds an already selected object.
600                                                 */
601
602                                                 button_release_can_deselect = false;
603                                         }
604                                 }
605
606                         } else {
607
608                                 if (press) {
609
610                                         if (selection->selected (clicked_routeview)) {
611                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
612                                         } else {
613                                                 all_equivalent_regions.push_back (clicked_regionview);
614                                         }
615
616                                         /* add all the equivalent regions, but only on button press */
617
618                                         if (!all_equivalent_regions.empty()) {
619                                                 commit = true;
620                                         }
621
622                                         selection->add (all_equivalent_regions);
623                                 }
624                         }
625                         break;
626
627                 case Selection::Set:
628                         if (!selection->selected (clicked_regionview)) {
629                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
630                                 selection->set (all_equivalent_regions);
631                                 commit = true;
632                         } else {
633                                 /* no commit necessary: clicked on an already selected region */
634                                 goto out;
635                         }
636                         break;
637
638                 default:
639                         /* silly compiler */
640                         break;
641                 }
642
643         } else if (op == Selection::Extend) {
644
645                 list<Selectable*> results;
646                 framepos_t last_frame;
647                 framepos_t first_frame;
648                 bool same_track = false;
649
650                 /* 1. find the last selected regionview in the track that was clicked in */
651
652                 last_frame = 0;
653                 first_frame = max_framepos;
654
655                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
656                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
657
658                                 if ((*x)->region()->last_frame() > last_frame) {
659                                         last_frame = (*x)->region()->last_frame();
660                                 }
661
662                                 if ((*x)->region()->first_frame() < first_frame) {
663                                         first_frame = (*x)->region()->first_frame();
664                                 }
665
666                                 same_track = true;
667                         }
668                 }
669
670                 if (same_track) {
671
672                         /* 2. figure out the boundaries for our search for new objects */
673
674                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
675                         case OverlapNone:
676                                 if (last_frame < clicked_regionview->region()->first_frame()) {
677                                         first_frame = last_frame;
678                                         last_frame = clicked_regionview->region()->last_frame();
679                                 } else {
680                                         last_frame = first_frame;
681                                         first_frame = clicked_regionview->region()->first_frame();
682                                 }
683                                 break;
684
685                         case OverlapExternal:
686                                 if (last_frame < clicked_regionview->region()->first_frame()) {
687                                         first_frame = last_frame;
688                                         last_frame = clicked_regionview->region()->last_frame();
689                                 } else {
690                                         last_frame = first_frame;
691                                         first_frame = clicked_regionview->region()->first_frame();
692                                 }
693                                 break;
694
695                         case OverlapInternal:
696                                 if (last_frame < clicked_regionview->region()->first_frame()) {
697                                         first_frame = last_frame;
698                                         last_frame = clicked_regionview->region()->last_frame();
699                                 } else {
700                                         last_frame = first_frame;
701                                         first_frame = clicked_regionview->region()->first_frame();
702                                 }
703                                 break;
704
705                         case OverlapStart:
706                         case OverlapEnd:
707                                 /* nothing to do except add clicked region to selection, since it
708                                    overlaps with the existing selection in this track.
709                                 */
710                                 break;
711                         }
712
713                 } else {
714
715                         /* click in a track that has no regions selected, so extend vertically
716                            to pick out all regions that are defined by the existing selection
717                            plus this one.
718                         */
719
720
721                         first_frame = clicked_regionview->region()->position();
722                         last_frame = clicked_regionview->region()->last_frame();
723
724                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
725                                 if ((*i)->region()->position() < first_frame) {
726                                         first_frame = (*i)->region()->position();
727                                 }
728                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
729                                         last_frame = (*i)->region()->last_frame();
730                                 }
731                         }
732                 }
733
734                 /* 2. find all the tracks we should select in */
735
736                 set<RouteTimeAxisView*> relevant_tracks;
737
738                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
739                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
740                         if (r) {
741                                 relevant_tracks.insert (r);
742                         }
743                 }
744
745                 set<RouteTimeAxisView*> already_in_selection;
746
747                 if (relevant_tracks.empty()) {
748
749                         /* no tracks selected .. thus .. if the
750                            regionview we're in isn't selected
751                            (i.e. we're about to extend to it), then
752                            find all tracks between the this one and
753                            any selected ones.
754                         */
755
756                         if (!selection->selected (clicked_regionview)) {
757
758                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
759
760                                 if (rtv) {
761
762                                         /* add this track to the ones we will search */
763
764                                         relevant_tracks.insert (rtv);
765
766                                         /* find the track closest to this one that
767                                            already a selected region.
768                                         */
769
770                                         RouteTimeAxisView* closest = 0;
771                                         int distance = INT_MAX;
772                                         int key = rtv->route()->order_key ("editor");
773
774                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
775
776                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
777
778                                                 if (artv && artv != rtv) {
779
780                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
781
782                                                         result = already_in_selection.insert (artv);
783
784                                                         if (result.second) {
785                                                                 /* newly added to already_in_selection */
786
787                                                                 int d = artv->route()->order_key ("editor");
788
789                                                                 d -= key;
790
791                                                                 if (abs (d) < distance) {
792                                                                         distance = abs (d);
793                                                                         closest = artv;
794                                                                 }
795                                                         }
796                                                 }
797                                         }
798
799                                         if (closest) {
800
801                                                 /* now add all tracks between that one and this one */
802
803                                                 int okey = closest->route()->order_key ("editor");
804
805                                                 if (okey > key) {
806                                                         swap (okey, key);
807                                                 }
808
809                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
810                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
811                                                         if (artv && artv != rtv) {
812
813                                                                 int k = artv->route()->order_key ("editor");
814
815                                                                 if (k >= okey && k <= key) {
816
817                                                                         /* in range but don't add it if
818                                                                            it already has tracks selected.
819                                                                            this avoids odd selection
820                                                                            behaviour that feels wrong.
821                                                                         */
822
823                                                                         if (find (already_in_selection.begin(),
824                                                                                   already_in_selection.end(),
825                                                                                   artv) == already_in_selection.end()) {
826
827                                                                                 relevant_tracks.insert (artv);
828                                                                         }
829                                                                 }
830                                                         }
831                                                 }
832                                         }
833                                 }
834                         }
835                 }
836
837                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
838                    one that was clicked.
839                 */
840
841                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
842                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
843                 }
844
845                 /* 4. convert to a vector of regions */
846
847                 vector<RegionView*> regions;
848
849                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
850                         RegionView* arv;
851
852                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
853                                 regions.push_back (arv);
854                         }
855                 }
856
857                 if (!regions.empty()) {
858                         selection->add (regions);
859                         commit = true;
860                 }
861         }
862
863 out:
864         return commit;
865 }
866
867
868 void
869 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
870 {
871         vector<RegionView*> all_equivalent_regions;
872
873         get_regions_corresponding_to (region, all_equivalent_regions);
874
875         if (all_equivalent_regions.empty()) {
876                 return;
877         }
878
879         begin_reversible_command (_("set selected regions"));
880
881         switch (op) {
882         case Selection::Toggle:
883                 /* XXX this is not correct */
884                 selection->toggle (all_equivalent_regions);
885                 break;
886         case Selection::Set:
887                 selection->set (all_equivalent_regions);
888                 break;
889         case Selection::Extend:
890                 selection->add (all_equivalent_regions);
891                 break;
892         case Selection::Add:
893                 selection->add (all_equivalent_regions);
894                 break;
895         }
896
897         commit_reversible_command () ;
898 }
899
900 bool
901 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
902 {
903         RegionView* rv;
904         boost::shared_ptr<Region> r (weak_r.lock());
905
906         if (!r) {
907                 return true;
908         }
909
910         if ((rv = sv->find_view (r)) == 0) {
911                 return true;
912         }
913
914         /* don't reset the selection if its something other than
915            a single other region.
916         */
917
918         if (selection->regions.size() > 1) {
919                 return true;
920         }
921
922         begin_reversible_command (_("set selected regions"));
923
924         selection->set (rv);
925
926         commit_reversible_command () ;
927
928         return true;
929 }
930
931 void
932 Editor::track_selection_changed ()
933 {
934         switch (selection->tracks.size()) {
935         case 0:
936                 break;
937         default:
938                 set_selected_mixer_strip (*(selection->tracks.front()));
939                 break;
940         }
941
942         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
943
944                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
945
946                 (*i)->set_selected (yn);
947
948                 TimeAxisView::Children c = (*i)->get_child_list ();
949                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
950                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
951                 }
952
953                 if (yn &&
954                     ((mouse_mode == MouseRange) ||
955                      ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
956                         (*i)->reshow_selection (selection->time);
957                 } else {
958                         (*i)->hide_selection ();
959                 }
960         }
961
962         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
963 }
964
965 void
966 Editor::time_selection_changed ()
967 {
968         if (Profile->get_sae()) {
969                 return;
970         }
971
972         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
973                 (*i)->hide_selection ();
974         }
975
976         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
977                 (*i)->show_selection (selection->time);
978         }
979
980         if (selection->time.empty()) {
981                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
982         } else {
983                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
984         }
985 }
986
987 /** Set all region actions to have a given sensitivity */
988 void
989 Editor::sensitize_all_region_actions (bool s)
990 {
991         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
992
993         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
994                 (*i)->set_sensitive (s);
995         }
996
997         _all_region_actions_sensitized = s;
998 }
999
1000 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1001  *  This method should be called just before displaying a Region menu.  When a Region menu is not
1002  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
1003  *  on entered_regionviews work without having to check sensitivity every time the selection or
1004  *  entered_regionview changes.
1005  *
1006  *  This method also sets up toggle action state as appropriate.
1007  */
1008 void
1009 Editor::sensitize_the_right_region_actions ()
1010 {
1011         if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1012                 sensitize_all_region_actions (false);
1013                 if (!selection->time.empty()) {
1014                         _region_actions->get_action("split-region")->set_sensitive (true);
1015                 }
1016                 return;
1017
1018         } else if (mouse_mode != MouseObject) {
1019                 sensitize_all_region_actions (false);
1020                 return;
1021         }
1022
1023         /* We get here if we are in Object mode */
1024
1025         RegionSelection rs = get_regions_from_selection_and_entered ();
1026         sensitize_all_region_actions (!rs.empty ());
1027
1028         _ignore_region_action = true;
1029
1030         /* Look through the regions that are selected and make notes about what we have got */
1031
1032         bool have_audio = false;
1033         bool have_multichannel_audio = false;
1034         bool have_midi = false;
1035         bool have_locked = false;
1036         bool have_unlocked = false;
1037         bool have_position_lock_style_audio = false;
1038         bool have_position_lock_style_music = false;
1039         bool have_muted = false;
1040         bool have_unmuted = false;
1041         bool have_opaque = false;
1042         bool have_non_opaque = false;
1043         bool have_not_at_natural_position = false;
1044         bool have_envelope_visible = false;
1045         bool have_envelope_invisible = false;
1046         bool have_envelope_active = false;
1047         bool have_envelope_inactive = false;
1048         bool have_non_unity_scale_amplitude = false;
1049         bool have_compound_regions = false;
1050
1051         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1052
1053                 boost::shared_ptr<Region> r = (*i)->region ();
1054                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1055
1056                 if (ar) {
1057                         have_audio = true;
1058                         if (ar->n_channels() > 1) {
1059                                 have_multichannel_audio = true;
1060                         }
1061                 }
1062
1063                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1064                         have_midi = true;
1065                 }
1066
1067                 if (r->is_compound()) {
1068                         have_compound_regions = true;
1069                 }
1070
1071                 if (r->locked()) {
1072                         have_locked = true;
1073                 } else {
1074                         have_unlocked = true;
1075                 }
1076
1077                 if (r->position_lock_style() == MusicTime) {
1078                         have_position_lock_style_music = true;
1079                 } else {
1080                         have_position_lock_style_audio = true;
1081                 }
1082
1083                 if (r->muted()) {
1084                         have_muted = true;
1085                 } else {
1086                         have_unmuted = true;
1087                 }
1088
1089                 if (r->opaque()) {
1090                         have_opaque = true;
1091                 } else {
1092                         have_non_opaque = true;
1093                 }
1094
1095                 if (!r->at_natural_position()) {
1096                         have_not_at_natural_position = true;
1097                 }
1098
1099                 if (ar) {
1100                         /* its a bit unfortunate that "envelope visible" is a view-only
1101                            property. we have to find the regionview to able to check
1102                            its current setting.
1103                         */
1104
1105                         have_envelope_invisible = false;
1106
1107                         if (*i) {
1108                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1109                                 if (arv) {
1110                                         if (arv->envelope_visible()) {
1111                                                 have_envelope_visible = true;
1112                                         } else {
1113                                                 have_envelope_invisible = true;
1114                                         }
1115                                 }
1116                         }
1117
1118                         if (ar->envelope_active()) {
1119                                 have_envelope_active = true;
1120                         } else {
1121                                 have_envelope_inactive = true;
1122                         }
1123
1124                         if (ar->scale_amplitude() != 1) {
1125                                 have_non_unity_scale_amplitude = true;
1126                         }
1127                 }
1128         }
1129
1130         if (rs.size() > 1) {
1131                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1132                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1133                 _region_actions->get_action("rename-region")->set_sensitive (false);
1134                 if (have_audio) {
1135                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1136                 } else {
1137                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1138                 }
1139         } else if (rs.size() == 1) {
1140                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1141                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1142                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1143         }
1144
1145         if (!have_multichannel_audio) {
1146                 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1147         }
1148
1149         if (!have_midi) {
1150                 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1151                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1152                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1153                 _region_actions->get_action("fork-region")->set_sensitive (false);
1154                 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1155                 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1156                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1157         } else {
1158                 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1159                 /* others were already marked sensitive */
1160         }
1161
1162         if (_edit_point == EditAtMouse) {
1163                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1164                 _region_actions->get_action("trim-front")->set_sensitive (false);
1165                 _region_actions->get_action("trim-back")->set_sensitive (false);
1166                 _region_actions->get_action("split-region")->set_sensitive (false);
1167                 _region_actions->get_action("place-transient")->set_sensitive (false);
1168         }
1169
1170         if (have_compound_regions) {
1171                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1172         } else {
1173                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1174         }
1175
1176         if (have_audio) {
1177
1178                 if (have_envelope_visible && !have_envelope_invisible) {
1179                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1180                 } else if (have_envelope_visible && have_envelope_invisible) {
1181                         // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1182                 }
1183
1184                 if (have_envelope_active && !have_envelope_inactive) {
1185                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1186                 } else if (have_envelope_active && have_envelope_inactive) {
1187                         // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1188                 }
1189
1190         } else {
1191
1192                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1193                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1194                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1195                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1196                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1197
1198         }
1199
1200         if (!have_non_unity_scale_amplitude || !have_audio) {
1201                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1202         }
1203
1204         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1205         if (have_locked && have_unlocked) {
1206                 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1207         }
1208
1209         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);
1210
1211         if (have_position_lock_style_music && have_position_lock_style_audio) {
1212                 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1213         }
1214
1215         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1216         if (have_muted && have_unmuted) {
1217                 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1218         }
1219
1220         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1221         if (have_opaque && have_non_opaque) {
1222                 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1223         }
1224
1225         if (!have_not_at_natural_position) {
1226                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1227         }
1228
1229         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1230         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1231                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1232         } else {
1233                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1234         }
1235
1236         _ignore_region_action = false;
1237
1238         _all_region_actions_sensitized = false;
1239 }
1240
1241
1242 void
1243 Editor::region_selection_changed ()
1244 {
1245         _regions->block_change_connection (true);
1246         editor_regions_selection_changed_connection.block(true);
1247
1248         if (_region_selection_change_updates_region_list) {
1249                 _regions->unselect_all ();
1250         }
1251
1252         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1253                 (*i)->set_selected_regionviews (selection->regions);
1254         }
1255
1256         if (_region_selection_change_updates_region_list) {
1257                 _regions->set_selected (selection->regions);
1258         }
1259
1260         _regions->block_change_connection (false);
1261         editor_regions_selection_changed_connection.block(false);
1262
1263         if (!_all_region_actions_sensitized) {
1264                 /* This selection change might have changed what region actions
1265                    are allowed, so sensitize them all in case a key is pressed.
1266                 */
1267                 sensitize_all_region_actions (true);
1268         }
1269 }
1270
1271 void
1272 Editor::point_selection_changed ()
1273 {
1274         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1275                 (*i)->set_selected_points (selection->points);
1276         }
1277 }
1278
1279 void
1280 Editor::select_all_in_track (Selection::Operation op)
1281 {
1282         list<Selectable *> touched;
1283
1284         if (!clicked_routeview) {
1285                 return;
1286         }
1287
1288         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1289
1290         switch (op) {
1291         case Selection::Toggle:
1292                 selection->add (touched);
1293                 break;
1294         case Selection::Set:
1295                 selection->set (touched);
1296                 break;
1297         case Selection::Extend:
1298                 /* meaningless, because we're selecting everything */
1299                 break;
1300         case Selection::Add:
1301                 selection->add (touched);
1302                 break;
1303         }
1304 }
1305
1306 void
1307 Editor::select_all_internal_edit (Selection::Operation)
1308 {
1309         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1310                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1311                 if (mrv) {
1312                         mrv->select_all_notes ();
1313                 }
1314         }
1315 }
1316
1317 void
1318 Editor::select_all (Selection::Operation op)
1319 {
1320         list<Selectable *> touched;
1321
1322         if (_internal_editing) {
1323                 select_all_internal_edit (op);
1324                 return;
1325         }
1326
1327         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1328                 if ((*iter)->hidden()) {
1329                         continue;
1330                 }
1331                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1332         }
1333         begin_reversible_command (_("select all"));
1334         switch (op) {
1335         case Selection::Add:
1336                 selection->add (touched);
1337                 break;
1338         case Selection::Toggle:
1339                 selection->add (touched);
1340                 break;
1341         case Selection::Set:
1342                 selection->set (touched);
1343                 break;
1344         case Selection::Extend:
1345                 /* meaningless, because we're selecting everything */
1346                 break;
1347         }
1348         commit_reversible_command ();
1349 }
1350
1351 void
1352 Editor::invert_selection_in_track ()
1353 {
1354         list<Selectable *> touched;
1355
1356         if (!clicked_routeview) {
1357                 return;
1358         }
1359
1360         clicked_routeview->get_inverted_selectables (*selection, touched);
1361         selection->set (touched);
1362 }
1363
1364 void
1365 Editor::invert_selection ()
1366 {
1367         list<Selectable *> touched;
1368
1369         if (_internal_editing) {
1370                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1371                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1372                         if (mrv) {
1373                                 mrv->invert_selection ();
1374                         }
1375                 }
1376                 return;
1377         }
1378
1379         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1380                 if ((*iter)->hidden()) {
1381                         continue;
1382                 }
1383                 (*iter)->get_inverted_selectables (*selection, touched);
1384         }
1385
1386         selection->set (touched);
1387 }
1388
1389 /** @param start Start time in session frames.
1390  *  @param end End time in session frames.
1391  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1392  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1393  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1394  *  within the region are already selected.
1395  */
1396 void
1397 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1398 {
1399         list<Selectable*> found;
1400
1401         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1402
1403                 if ((*iter)->hidden()) {
1404                         continue;
1405                 }
1406
1407                 (*iter)->get_selectables (start, end, top, bot, found);
1408         }
1409
1410         if (found.empty()) {
1411                 return;
1412         }
1413
1414         if (preserve_if_selected && op != Selection::Toggle) {
1415                 list<Selectable*>::iterator i = found.begin();
1416                 while (i != found.end() && (*i)->get_selected()) {
1417                         ++i;
1418                 }
1419
1420                 if (i == found.end()) {
1421                         return;
1422                 }
1423         }
1424
1425         begin_reversible_command (_("select all within"));
1426         switch (op) {
1427         case Selection::Add:
1428                 selection->add (found);
1429                 break;
1430         case Selection::Toggle:
1431                 selection->toggle (found);
1432                 break;
1433         case Selection::Set:
1434                 selection->set (found);
1435                 break;
1436         case Selection::Extend:
1437                 /* not defined yet */
1438                 break;
1439         }
1440
1441         commit_reversible_command ();
1442 }
1443
1444 void
1445 Editor::set_selection_from_region ()
1446 {
1447         if (selection->regions.empty()) {
1448                 return;
1449         }
1450
1451         selection->set (selection->regions.start(), selection->regions.end_frame());
1452         if (!Profile->get_sae()) {
1453                 set_mouse_mode (Editing::MouseRange, false);
1454         }
1455 }
1456
1457 void
1458 Editor::set_selection_from_punch()
1459 {
1460         Location* location;
1461
1462         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1463                 return;
1464         }
1465
1466         set_selection_from_range (*location);
1467 }
1468
1469 void
1470 Editor::set_selection_from_loop()
1471 {
1472         Location* location;
1473
1474         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1475                 return;
1476         }
1477         set_selection_from_range (*location);
1478 }
1479
1480 void
1481 Editor::set_selection_from_range (Location& loc)
1482 {
1483         begin_reversible_command (_("set selection from range"));
1484         selection->set (loc.start(), loc.end());
1485         commit_reversible_command ();
1486
1487         if (!Profile->get_sae()) {
1488                 set_mouse_mode (Editing::MouseRange, false);
1489         }
1490 }
1491
1492 void
1493 Editor::select_all_selectables_using_time_selection ()
1494 {
1495         list<Selectable *> touched;
1496
1497         if (selection->time.empty()) {
1498                 return;
1499         }
1500
1501         framepos_t start = selection->time[clicked_selection].start;
1502         framepos_t end = selection->time[clicked_selection].end;
1503
1504         if (end - start < 1)  {
1505                 return;
1506         }
1507
1508         TrackViewList* ts;
1509
1510         if (selection->tracks.empty()) {
1511                 ts = &track_views;
1512         } else {
1513                 ts = &selection->tracks;
1514         }
1515
1516         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1517                 if ((*iter)->hidden()) {
1518                         continue;
1519                 }
1520                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1521         }
1522
1523         begin_reversible_command (_("select all from range"));
1524         selection->set (touched);
1525         commit_reversible_command ();
1526 }
1527
1528
1529 void
1530 Editor::select_all_selectables_using_punch()
1531 {
1532         Location* location = _session->locations()->auto_punch_location();
1533         list<Selectable *> touched;
1534
1535         if (location == 0 || (location->end() - location->start() <= 1))  {
1536                 return;
1537         }
1538
1539
1540         TrackViewList* ts;
1541
1542         if (selection->tracks.empty()) {
1543                 ts = &track_views;
1544         } else {
1545                 ts = &selection->tracks;
1546         }
1547
1548         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1549                 if ((*iter)->hidden()) {
1550                         continue;
1551                 }
1552                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1553         }
1554         begin_reversible_command (_("select all from punch"));
1555         selection->set (touched);
1556         commit_reversible_command ();
1557
1558 }
1559
1560 void
1561 Editor::select_all_selectables_using_loop()
1562 {
1563         Location* location = _session->locations()->auto_loop_location();
1564         list<Selectable *> touched;
1565
1566         if (location == 0 || (location->end() - location->start() <= 1))  {
1567                 return;
1568         }
1569
1570
1571         TrackViewList* ts;
1572
1573         if (selection->tracks.empty()) {
1574                 ts = &track_views;
1575         } else {
1576                 ts = &selection->tracks;
1577         }
1578
1579         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1580                 if ((*iter)->hidden()) {
1581                         continue;
1582                 }
1583                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1584         }
1585         begin_reversible_command (_("select all from loop"));
1586         selection->set (touched);
1587         commit_reversible_command ();
1588
1589 }
1590
1591 void
1592 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1593 {
1594         framepos_t start;
1595         framepos_t end;
1596         list<Selectable *> touched;
1597
1598         if (after) {
1599                 start = cursor->current_frame;
1600                 end = _session->current_end_frame();
1601         } else {
1602                 if (cursor->current_frame > 0) {
1603                         start = 0;
1604                         end = cursor->current_frame - 1;
1605                 } else {
1606                         return;
1607                 }
1608         }
1609
1610         if (_internal_editing) {
1611                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1612                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1613                         if (mrv) {
1614                                 mrv->select_range (start, end);
1615                         }
1616                 }
1617                 return;
1618         }
1619
1620         if (after) {
1621                 begin_reversible_command (_("select all after cursor"));
1622         } else {
1623                 begin_reversible_command (_("select all before cursor"));
1624         }
1625
1626         TrackViewList* ts;
1627
1628         if (selection->tracks.empty()) {
1629                 ts = &track_views;
1630         } else {
1631                 ts = &selection->tracks;
1632         }
1633
1634         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1635                 if ((*iter)->hidden()) {
1636                         continue;
1637                 }
1638                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1639         }
1640         selection->set (touched);
1641         commit_reversible_command ();
1642 }
1643
1644 void
1645 Editor::select_all_selectables_using_edit (bool after)
1646 {
1647         framepos_t start;
1648         framepos_t end;
1649         list<Selectable *> touched;
1650
1651         if (after) {
1652                 start = get_preferred_edit_position();
1653                 end = _session->current_end_frame();
1654         } else {
1655                 if ((end = get_preferred_edit_position()) > 1) {
1656                         start = 0;
1657                         end -= 1;
1658                 } else {
1659                         return;
1660                 }
1661         }
1662
1663         if (_internal_editing) {
1664                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1665                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1666                         mrv->select_range (start, end);
1667                 }
1668                 return;
1669         }
1670
1671         if (after) {
1672                 begin_reversible_command (_("select all after edit"));
1673         } else {
1674                 begin_reversible_command (_("select all before edit"));
1675         }
1676
1677         TrackViewList* ts;
1678
1679         if (selection->tracks.empty()) {
1680                 ts = &track_views;
1681         } else {
1682                 ts = &selection->tracks;
1683         }
1684
1685         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1686                 if ((*iter)->hidden()) {
1687                         continue;
1688                 }
1689                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1690         }
1691         selection->set (touched);
1692         commit_reversible_command ();
1693 }
1694
1695 void
1696 Editor::select_all_selectables_between (bool /*within*/)
1697 {
1698         framepos_t start;
1699         framepos_t end;
1700         list<Selectable *> touched;
1701
1702         if (!get_edit_op_range (start, end)) {
1703                 return;
1704         }
1705
1706         if (_internal_editing) {
1707                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1708                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1709                         mrv->select_range (start, end);
1710                 }
1711                 return;
1712         }
1713
1714         TrackViewList* ts;
1715
1716         if (selection->tracks.empty()) {
1717                 ts = &track_views;
1718         } else {
1719                 ts = &selection->tracks;
1720         }
1721
1722         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1723                 if ((*iter)->hidden()) {
1724                         continue;
1725                 }
1726                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1727         }
1728
1729         selection->set (touched);
1730 }
1731
1732 void
1733 Editor::select_range_between ()
1734 {
1735         framepos_t start;
1736         framepos_t end;
1737
1738         if (mouse_mode == MouseRange && !selection->time.empty()) {
1739                 selection->clear_time ();
1740         }
1741
1742         if (!get_edit_op_range (start, end)) {
1743                 return;
1744         }
1745
1746         set_mouse_mode (MouseRange);
1747         selection->set (start, end);
1748 }
1749
1750 bool
1751 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1752 {
1753         framepos_t m;
1754         bool ignored;
1755
1756         /* in range mode, use any existing selection */
1757
1758         if (mouse_mode == MouseRange && !selection->time.empty()) {
1759                 /* we know that these are ordered */
1760                 start = selection->time.start();
1761                 end = selection->time.end_frame();
1762                 return true;
1763         }
1764
1765         if (!mouse_frame (m, ignored)) {
1766                 /* mouse is not in a canvas, try playhead+selected marker.
1767                    this is probably most true when using menus.
1768                 */
1769
1770                 if (selection->markers.empty()) {
1771                         return false;
1772                 }
1773
1774                 start = selection->markers.front()->position();
1775                 end = _session->audible_frame();
1776
1777         } else {
1778
1779                 switch (_edit_point) {
1780                 case EditAtPlayhead:
1781                         if (selection->markers.empty()) {
1782                                 /* use mouse + playhead */
1783                                 start = m;
1784                                 end = _session->audible_frame();
1785                         } else {
1786                                 /* use playhead + selected marker */
1787                                 start = _session->audible_frame();
1788                                 end = selection->markers.front()->position();
1789                         }
1790                         break;
1791
1792                 case EditAtMouse:
1793                         /* use mouse + selected marker */
1794                         if (selection->markers.empty()) {
1795                                 start = m;
1796                                 end = _session->audible_frame();
1797                         } else {
1798                                 start = selection->markers.front()->position();
1799                                 end = m;
1800                         }
1801                         break;
1802
1803                 case EditAtSelectedMarker:
1804                         /* use mouse + selected marker */
1805                         if (selection->markers.empty()) {
1806
1807                                 MessageDialog win (_("No edit range defined"),
1808                                                    false,
1809                                                    MESSAGE_INFO,
1810                                                    BUTTONS_OK);
1811
1812                                 win.set_secondary_text (
1813                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1814
1815
1816                                 win.set_default_response (RESPONSE_CLOSE);
1817                                 win.set_position (Gtk::WIN_POS_MOUSE);
1818                                 win.show_all();
1819
1820                                 win.run ();
1821
1822                                 return false; // NO RANGE
1823                         }
1824                         start = selection->markers.front()->position();
1825                         end = m;
1826                         break;
1827                 }
1828         }
1829
1830         if (start == end) {
1831                 return false;
1832         }
1833
1834         if (start > end) {
1835                 swap (start, end);
1836         }
1837
1838         /* turn range into one delimited by start...end,
1839            not start...end-1
1840         */
1841
1842         end++;
1843
1844         return true;
1845 }
1846
1847 void
1848 Editor::deselect_all ()
1849 {
1850         selection->clear ();
1851 }
1852
1853 long
1854 Editor::select_range_around_region (RegionView* rv)
1855 {
1856         assert (rv);
1857
1858         selection->set (&rv->get_time_axis_view());
1859
1860         selection->time.clear ();
1861         boost::shared_ptr<Region> r = rv->region ();
1862         return selection->set (r->position(), r->position() + r->length());
1863 }