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