5875fffbfacf77d6c986feab43e05936b4578bba
[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                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1089                 _region_actions->get_action("trim-front")->set_sensitive (false);
1090                 _region_actions->get_action("trim-back")->set_sensitive (false);
1091                 _region_actions->get_action("split-region")->set_sensitive (false);
1092                 _region_actions->get_action("place-transient")->set_sensitive (false);
1093         }
1094
1095         if (have_compound_regions) {
1096                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1097         } else {
1098                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1099         }
1100
1101         if (have_audio) {
1102
1103                 if (have_envelope_visible && !have_envelope_invisible) {
1104                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1105                 } else if (have_envelope_visible && have_envelope_invisible) {
1106                         // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1107                 }
1108
1109                 if (have_envelope_active && !have_envelope_inactive) {
1110                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1111                 } else if (have_envelope_active && have_envelope_inactive) {
1112                         // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1113                 }
1114
1115         } else {
1116
1117                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1118                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1119                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1120                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1121                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1122
1123         }
1124
1125         if (!have_non_unity_scale_amplitude || !have_audio) {
1126                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1127         }
1128
1129         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1130         if (have_locked && have_unlocked) {
1131                 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1132         }
1133
1134         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);
1135
1136         if (have_position_lock_style_music && have_position_lock_style_audio) {
1137                 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1138         }
1139
1140         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1141         if (have_muted && have_unmuted) {
1142                 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1143         }
1144
1145         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1146         if (have_opaque && have_non_opaque) {
1147                 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1148         }
1149
1150         if (!have_not_at_natural_position) {
1151                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1152         }
1153
1154         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1155         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1156                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1157         } else {
1158                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1159         }
1160
1161         _ignore_region_action = false;
1162
1163         _all_region_actions_sensitized = false;
1164 }
1165
1166
1167 void
1168 Editor::region_selection_changed ()
1169 {
1170         _regions->block_change_connection (true);
1171         editor_regions_selection_changed_connection.block(true);
1172
1173         if (_region_selection_change_updates_region_list) {
1174                 _regions->unselect_all ();
1175         }
1176
1177         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1178                 (*i)->set_selected_regionviews (selection->regions);
1179         }
1180
1181         if (_region_selection_change_updates_region_list) {
1182                 _regions->set_selected (selection->regions);
1183         }
1184
1185         _regions->block_change_connection (false);
1186         editor_regions_selection_changed_connection.block(false);
1187
1188         if (!_all_region_actions_sensitized) {
1189                 /* This selection change might have changed what region actions
1190                    are allowed, so sensitize them all in case a key is pressed.
1191                 */
1192                 sensitize_all_region_actions (true);
1193         }
1194 }
1195
1196 void
1197 Editor::point_selection_changed ()
1198 {
1199         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1200                 (*i)->set_selected_points (selection->points);
1201         }
1202 }
1203
1204 void
1205 Editor::select_all_in_track (Selection::Operation op)
1206 {
1207         list<Selectable *> touched;
1208
1209         if (!clicked_routeview) {
1210                 return;
1211         }
1212
1213         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1214
1215         switch (op) {
1216         case Selection::Toggle:
1217                 selection->add (touched);
1218                 break;
1219         case Selection::Set:
1220                 selection->set (touched);
1221                 break;
1222         case Selection::Extend:
1223                 /* meaningless, because we're selecting everything */
1224                 break;
1225         case Selection::Add:
1226                 selection->add (touched);
1227                 break;
1228         }
1229 }
1230
1231 void
1232 Editor::select_all_internal_edit (Selection::Operation)
1233 {
1234         /* currently limited to MIDI only */
1235
1236         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1237                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1238                 if (mrv) {
1239                         mrv->select_all_notes ();
1240                 }
1241         }
1242 }
1243
1244 void
1245 Editor::select_all (Selection::Operation op)
1246 {
1247         list<Selectable *> touched;
1248
1249         if (_internal_editing) {
1250                 select_all_internal_edit (op);
1251                 return;
1252         }
1253
1254         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1255                 if ((*iter)->hidden()) {
1256                         continue;
1257                 }
1258                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1259         }
1260         begin_reversible_command (_("select all"));
1261         switch (op) {
1262         case Selection::Add:
1263                 selection->add (touched);
1264                 break;
1265         case Selection::Toggle:
1266                 selection->add (touched);
1267                 break;
1268         case Selection::Set:
1269                 selection->set (touched);
1270                 break;
1271         case Selection::Extend:
1272                 /* meaningless, because we're selecting everything */
1273                 break;
1274         }
1275         commit_reversible_command ();
1276 }
1277 void
1278 Editor::invert_selection_in_track ()
1279 {
1280         list<Selectable *> touched;
1281
1282         if (!clicked_routeview) {
1283                 return;
1284         }
1285
1286         clicked_routeview->get_inverted_selectables (*selection, touched);
1287         selection->set (touched);
1288 }
1289
1290 void
1291 Editor::invert_selection ()
1292 {
1293         list<Selectable *> touched;
1294
1295         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1296                 if ((*iter)->hidden()) {
1297                         continue;
1298                 }
1299                 (*iter)->get_inverted_selectables (*selection, touched);
1300         }
1301
1302         selection->set (touched);
1303 }
1304
1305 /** @param start Start time in session frames.
1306  *  @param end End time in session frames.
1307  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1308  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1309  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1310  *  within the region are already selected.
1311  */
1312 void
1313 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1314 {
1315         list<Selectable*> found;
1316
1317         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1318
1319                 if ((*iter)->hidden()) {
1320                         continue;
1321                 }
1322
1323                 (*iter)->get_selectables (start, end, top, bot, found);
1324         }
1325
1326         if (found.empty()) {
1327                 return;
1328         }
1329
1330         if (preserve_if_selected && op != Selection::Toggle) {
1331                 list<Selectable*>::iterator i = found.begin();
1332                 while (i != found.end() && (*i)->get_selected()) {
1333                         ++i;
1334                 }
1335
1336                 if (i == found.end()) {
1337                         return;
1338                 }
1339         }
1340
1341         begin_reversible_command (_("select all within"));
1342         switch (op) {
1343         case Selection::Add:
1344                 selection->add (found);
1345                 break;
1346         case Selection::Toggle:
1347                 selection->toggle (found);
1348                 break;
1349         case Selection::Set:
1350                 selection->set (found);
1351                 break;
1352         case Selection::Extend:
1353                 /* not defined yet */
1354                 break;
1355         }
1356
1357         commit_reversible_command ();
1358 }
1359
1360 void
1361 Editor::set_selection_from_region ()
1362 {
1363         if (selection->regions.empty()) {
1364                 return;
1365         }
1366
1367         selection->set (selection->regions.start(), selection->regions.end_frame());
1368         if (!Profile->get_sae()) {
1369                 set_mouse_mode (Editing::MouseRange, false);
1370         }
1371 }
1372
1373 void
1374 Editor::set_selection_from_punch()
1375 {
1376         Location* location;
1377
1378         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1379                 return;
1380         }
1381
1382         set_selection_from_range (*location);
1383 }
1384
1385 void
1386 Editor::set_selection_from_loop()
1387 {
1388         Location* location;
1389
1390         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1391                 return;
1392         }
1393         set_selection_from_range (*location);
1394 }
1395
1396 void
1397 Editor::set_selection_from_range (Location& loc)
1398 {
1399         begin_reversible_command (_("set selection from range"));
1400         selection->set (loc.start(), loc.end());
1401         commit_reversible_command ();
1402
1403         if (!Profile->get_sae()) {
1404                 set_mouse_mode (Editing::MouseRange, false);
1405         }
1406 }
1407
1408 void
1409 Editor::select_all_selectables_using_time_selection ()
1410 {
1411         list<Selectable *> touched;
1412
1413         if (selection->time.empty()) {
1414                 return;
1415         }
1416
1417         framepos_t start = selection->time[clicked_selection].start;
1418         framepos_t end = selection->time[clicked_selection].end;
1419
1420         if (end - start < 1)  {
1421                 return;
1422         }
1423
1424         TrackViewList* ts;
1425
1426         if (selection->tracks.empty()) {
1427                 ts = &track_views;
1428         } else {
1429                 ts = &selection->tracks;
1430         }
1431
1432         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1433                 if ((*iter)->hidden()) {
1434                         continue;
1435                 }
1436                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1437         }
1438
1439         begin_reversible_command (_("select all from range"));
1440         selection->set (touched);
1441         commit_reversible_command ();
1442 }
1443
1444
1445 void
1446 Editor::select_all_selectables_using_punch()
1447 {
1448         Location* location = _session->locations()->auto_punch_location();
1449         list<Selectable *> touched;
1450
1451         if (location == 0 || (location->end() - location->start() <= 1))  {
1452                 return;
1453         }
1454
1455
1456         TrackViewList* ts;
1457
1458         if (selection->tracks.empty()) {
1459                 ts = &track_views;
1460         } else {
1461                 ts = &selection->tracks;
1462         }
1463
1464         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1465                 if ((*iter)->hidden()) {
1466                         continue;
1467                 }
1468                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1469         }
1470         begin_reversible_command (_("select all from punch"));
1471         selection->set (touched);
1472         commit_reversible_command ();
1473
1474 }
1475
1476 void
1477 Editor::select_all_selectables_using_loop()
1478 {
1479         Location* location = _session->locations()->auto_loop_location();
1480         list<Selectable *> touched;
1481
1482         if (location == 0 || (location->end() - location->start() <= 1))  {
1483                 return;
1484         }
1485
1486
1487         TrackViewList* ts;
1488
1489         if (selection->tracks.empty()) {
1490                 ts = &track_views;
1491         } else {
1492                 ts = &selection->tracks;
1493         }
1494
1495         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1496                 if ((*iter)->hidden()) {
1497                         continue;
1498                 }
1499                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1500         }
1501         begin_reversible_command (_("select all from loop"));
1502         selection->set (touched);
1503         commit_reversible_command ();
1504
1505 }
1506
1507 void
1508 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1509 {
1510         framepos_t start;
1511         framepos_t end;
1512         list<Selectable *> touched;
1513
1514         if (after) {
1515                 begin_reversible_command (_("select all after cursor"));
1516                 start = cursor->current_frame;
1517                 end = _session->current_end_frame();
1518         } else {
1519                 if (cursor->current_frame > 0) {
1520                         begin_reversible_command (_("select all before cursor"));
1521                         start = 0;
1522                         end = cursor->current_frame - 1;
1523                 } else {
1524                         return;
1525                 }
1526         }
1527
1528
1529         TrackViewList* ts;
1530
1531         if (selection->tracks.empty()) {
1532                 ts = &track_views;
1533         } else {
1534                 ts = &selection->tracks;
1535         }
1536
1537         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1538                 if ((*iter)->hidden()) {
1539                         continue;
1540                 }
1541                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1542         }
1543         selection->set (touched);
1544         commit_reversible_command ();
1545 }
1546
1547 void
1548 Editor::select_all_selectables_using_edit (bool after)
1549 {
1550         framepos_t start;
1551         framepos_t end;
1552         list<Selectable *> touched;
1553
1554         if (after) {
1555                 begin_reversible_command (_("select all after edit"));
1556                 start = get_preferred_edit_position();
1557                 end = _session->current_end_frame();
1558         } else {
1559                 if ((end = get_preferred_edit_position()) > 1) {
1560                         begin_reversible_command (_("select all before edit"));
1561                         start = 0;
1562                         end -= 1;
1563                 } else {
1564                         return;
1565                 }
1566         }
1567
1568
1569         TrackViewList* ts;
1570
1571         if (selection->tracks.empty()) {
1572                 ts = &track_views;
1573         } else {
1574                 ts = &selection->tracks;
1575         }
1576
1577         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1578                 if ((*iter)->hidden()) {
1579                         continue;
1580                 }
1581                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1582         }
1583         selection->set (touched);
1584         commit_reversible_command ();
1585 }
1586
1587 void
1588 Editor::select_all_selectables_between (bool /*within*/)
1589 {
1590         framepos_t start;
1591         framepos_t end;
1592         list<Selectable *> touched;
1593
1594         if (!get_edit_op_range (start, end)) {
1595                 return;
1596         }
1597
1598         TrackViewList* ts;
1599
1600         if (selection->tracks.empty()) {
1601                 ts = &track_views;
1602         } else {
1603                 ts = &selection->tracks;
1604         }
1605
1606         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1607                 if ((*iter)->hidden()) {
1608                         continue;
1609                 }
1610                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1611         }
1612
1613         selection->set (touched);
1614 }
1615
1616 void
1617 Editor::select_range_between ()
1618 {
1619         framepos_t start;
1620         framepos_t end;
1621
1622         if (mouse_mode == MouseRange && !selection->time.empty()) {
1623                 selection->clear_time ();
1624         }
1625
1626         if (!get_edit_op_range (start, end)) {
1627                 return;
1628         }
1629
1630         set_mouse_mode (MouseRange);
1631         selection->set (start, end);
1632 }
1633
1634 bool
1635 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1636 {
1637         framepos_t m;
1638         bool ignored;
1639
1640         /* in range mode, use any existing selection */
1641
1642         if (mouse_mode == MouseRange && !selection->time.empty()) {
1643                 /* we know that these are ordered */
1644                 start = selection->time.start();
1645                 end = selection->time.end_frame();
1646                 return true;
1647         }
1648
1649         if (!mouse_frame (m, ignored)) {
1650                 /* mouse is not in a canvas, try playhead+selected marker.
1651                    this is probably most true when using menus.
1652                 */
1653
1654                 if (selection->markers.empty()) {
1655                         return false;
1656                 }
1657
1658                 start = selection->markers.front()->position();
1659                 end = _session->audible_frame();
1660
1661         } else {
1662
1663                 switch (_edit_point) {
1664                 case EditAtPlayhead:
1665                         if (selection->markers.empty()) {
1666                                 /* use mouse + playhead */
1667                                 start = m;
1668                                 end = _session->audible_frame();
1669                         } else {
1670                                 /* use playhead + selected marker */
1671                                 start = _session->audible_frame();
1672                                 end = selection->markers.front()->position();
1673                         }
1674                         break;
1675
1676                 case EditAtMouse:
1677                         /* use mouse + selected marker */
1678                         if (selection->markers.empty()) {
1679                                 start = m;
1680                                 end = _session->audible_frame();
1681                         } else {
1682                                 start = selection->markers.front()->position();
1683                                 end = m;
1684                         }
1685                         break;
1686
1687                 case EditAtSelectedMarker:
1688                         /* use mouse + selected marker */
1689                         if (selection->markers.empty()) {
1690
1691                                 MessageDialog win (_("No edit range defined"),
1692                                                    false,
1693                                                    MESSAGE_INFO,
1694                                                    BUTTONS_OK);
1695
1696                                 win.set_secondary_text (
1697                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1698
1699
1700                                 win.set_default_response (RESPONSE_CLOSE);
1701                                 win.set_position (Gtk::WIN_POS_MOUSE);
1702                                 win.show_all();
1703
1704                                 win.run ();
1705
1706                                 return false; // NO RANGE
1707                         }
1708                         start = selection->markers.front()->position();
1709                         end = m;
1710                         break;
1711                 }
1712         }
1713
1714         if (start == end) {
1715                 return false;
1716         }
1717
1718         if (start > end) {
1719                 swap (start, end);
1720         }
1721
1722         /* turn range into one delimited by start...end,
1723            not start...end-1
1724         */
1725
1726         end++;
1727
1728         return true;
1729 }
1730
1731 void
1732 Editor::deselect_all ()
1733 {
1734         selection->clear ();
1735 }
1736
1737 long
1738 Editor::select_range_around_region (RegionView* rv)
1739 {
1740         assert (rv);
1741
1742         selection->set (&rv->get_time_axis_view());
1743
1744         selection->time.clear ();
1745         boost::shared_ptr<Region> r = rv->region ();
1746         return selection->set (r->position(), r->position() + r->length());
1747 }