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