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