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