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