Hack round a maybe delightful (for some values of delightful) and certainly evil...
[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
457                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
458                         equivalent.add (*j);
459                 }
460
461                 equivalent.add (*i);
462         }
463
464         return equivalent;
465 }
466
467
468 int
469 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
470 {
471         int region_count = 0;
472
473         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
474
475                 RouteTimeAxisView* tatv;
476
477                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
478
479                         boost::shared_ptr<Playlist> pl;
480                         vector<boost::shared_ptr<Region> > results;
481                         RegionView* marv;
482                         boost::shared_ptr<Track> tr;
483
484                         if ((tr = tatv->track()) == 0) {
485                                 /* bus */
486                                 continue;
487                         }
488
489                         if ((pl = (tr->playlist())) != 0) {
490                                 pl->get_region_list_equivalent_regions (region, results);
491                         }
492
493                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
494                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
495                                         region_count++;
496                                 }
497                         }
498
499                 }
500         }
501
502         return region_count;
503 }
504
505
506 bool
507 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
508 {
509         vector<RegionView*> all_equivalent_regions;
510         bool commit = false;
511
512         if (!clicked_regionview || !clicked_routeview) {
513                 return false;
514         }
515
516         if (press) {
517                 button_release_can_deselect = false;
518         }
519
520         if (op == Selection::Toggle || op == Selection::Set) {
521
522
523                 switch (op) {
524                 case Selection::Toggle:
525
526                         if (selection->selected (clicked_regionview)) {
527                                 if (press) {
528
529                                         /* whatever was clicked was selected already; do nothing here but allow
530                                            the button release to deselect it
531                                         */
532
533                                         button_release_can_deselect = true;
534
535                                 } else {
536
537                                         if (button_release_can_deselect) {
538
539                                                 /* just remove this one region, but only on a permitted button release */
540
541                                                 selection->remove (clicked_regionview);
542                                                 commit = true;
543
544                                                 /* no more deselect action on button release till a new press
545                                                    finds an already selected object.
546                                                 */
547
548                                                 button_release_can_deselect = false;
549                                         }
550                                 }
551
552                         } else {
553
554                                 if (press) {
555
556                                         if (selection->selected (clicked_routeview)) {
557                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
558                                         } else {
559                                                 all_equivalent_regions.push_back (clicked_regionview);
560                                         }
561
562                                         /* add all the equivalent regions, but only on button press */
563
564                                         if (!all_equivalent_regions.empty()) {
565                                                 commit = true;
566                                         }
567
568                                         selection->add (all_equivalent_regions);
569                                 }
570                         }
571                         break;
572
573                 case Selection::Set:
574                         if (!selection->selected (clicked_regionview)) {
575                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
576                                 selection->set (all_equivalent_regions);
577                                 commit = true;
578                         } else {
579                                 /* no commit necessary: clicked on an already selected region */
580                                 goto out;
581                         }
582                         break;
583
584                 default:
585                         /* silly compiler */
586                         break;
587                 }
588
589         } else if (op == Selection::Extend) {
590
591                 list<Selectable*> results;
592                 framepos_t last_frame;
593                 framepos_t first_frame;
594                 bool same_track = false;
595
596                 /* 1. find the last selected regionview in the track that was clicked in */
597
598                 last_frame = 0;
599                 first_frame = max_framepos;
600
601                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
602                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
603
604                                 if ((*x)->region()->last_frame() > last_frame) {
605                                         last_frame = (*x)->region()->last_frame();
606                                 }
607
608                                 if ((*x)->region()->first_frame() < first_frame) {
609                                         first_frame = (*x)->region()->first_frame();
610                                 }
611
612                                 same_track = true;
613                         }
614                 }
615
616                 if (same_track) {
617
618                         /* 2. figure out the boundaries for our search for new objects */
619
620                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
621                         case OverlapNone:
622                                 if (last_frame < clicked_regionview->region()->first_frame()) {
623                                         first_frame = last_frame;
624                                         last_frame = clicked_regionview->region()->last_frame();
625                                 } else {
626                                         last_frame = first_frame;
627                                         first_frame = clicked_regionview->region()->first_frame();
628                                 }
629                                 break;
630
631                         case OverlapExternal:
632                                 if (last_frame < clicked_regionview->region()->first_frame()) {
633                                         first_frame = last_frame;
634                                         last_frame = clicked_regionview->region()->last_frame();
635                                 } else {
636                                         last_frame = first_frame;
637                                         first_frame = clicked_regionview->region()->first_frame();
638                                 }
639                                 break;
640
641                         case OverlapInternal:
642                                 if (last_frame < clicked_regionview->region()->first_frame()) {
643                                         first_frame = last_frame;
644                                         last_frame = clicked_regionview->region()->last_frame();
645                                 } else {
646                                         last_frame = first_frame;
647                                         first_frame = clicked_regionview->region()->first_frame();
648                                 }
649                                 break;
650
651                         case OverlapStart:
652                         case OverlapEnd:
653                                 /* nothing to do except add clicked region to selection, since it
654                                    overlaps with the existing selection in this track.
655                                 */
656                                 break;
657                         }
658
659                 } else {
660
661                         /* click in a track that has no regions selected, so extend vertically
662                            to pick out all regions that are defined by the existing selection
663                            plus this one.
664                         */
665
666
667                         first_frame = clicked_regionview->region()->position();
668                         last_frame = clicked_regionview->region()->last_frame();
669
670                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
671                                 if ((*i)->region()->position() < first_frame) {
672                                         first_frame = (*i)->region()->position();
673                                 }
674                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
675                                         last_frame = (*i)->region()->last_frame();
676                                 }
677                         }
678                 }
679
680                 /* 2. find all the tracks we should select in */
681
682                 set<RouteTimeAxisView*> relevant_tracks;
683
684                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
685                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
686                         if (r) {
687                                 relevant_tracks.insert (r);
688                         }
689                 }
690                 
691                 set<RouteTimeAxisView*> already_in_selection;
692
693                 if (relevant_tracks.empty()) {
694
695                         /* no tracks selected .. thus .. if the
696                            regionview we're in isn't selected
697                            (i.e. we're about to extend to it), then
698                            find all tracks between the this one and
699                            any selected ones.
700                         */
701
702                         if (!selection->selected (clicked_regionview)) {
703
704                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
705
706                                 if (rtv) {
707
708                                         /* add this track to the ones we will search */
709
710                                         relevant_tracks.insert (rtv);
711
712                                         /* find the track closest to this one that
713                                            already a selected region.
714                                         */
715
716                                         RouteTimeAxisView* closest = 0;
717                                         int distance = INT_MAX;
718                                         int key = rtv->route()->order_key ("editor");
719
720                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
721
722                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
723
724                                                 if (artv && artv != rtv) {
725
726                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
727
728                                                         result = already_in_selection.insert (artv);
729
730                                                         if (result.second) {
731                                                                 /* newly added to already_in_selection */
732
733                                                                 int d = artv->route()->order_key ("editor");
734
735                                                                 d -= key;
736
737                                                                 if (abs (d) < distance) {
738                                                                         distance = abs (d);
739                                                                         closest = artv;
740                                                                 }
741                                                         }
742                                                 }
743                                         }
744
745                                         if (closest) {
746
747                                                 /* now add all tracks between that one and this one */
748
749                                                 int okey = closest->route()->order_key ("editor");
750
751                                                 if (okey > key) {
752                                                         swap (okey, key);
753                                                 }
754
755                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
756                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
757                                                         if (artv && artv != rtv) {
758
759                                                                 int k = artv->route()->order_key ("editor");
760
761                                                                 if (k >= okey && k <= key) {
762
763                                                                         /* in range but don't add it if
764                                                                            it already has tracks selected.
765                                                                            this avoids odd selection
766                                                                            behaviour that feels wrong.
767                                                                         */
768
769                                                                         if (find (already_in_selection.begin(),
770                                                                                   already_in_selection.end(),
771                                                                                   artv) == already_in_selection.end()) {
772
773                                                                                 relevant_tracks.insert (artv);
774                                                                         }
775                                                                 }
776                                                         }
777                                                 }
778                                         }
779                                 }
780                         }
781                 }
782
783                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
784                            one that was clicked.
785                 */
786
787                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
788                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
789                 }
790
791                 /* 4. convert to a vector of regions */
792
793                 vector<RegionView*> regions;
794
795                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
796                         RegionView* arv;
797
798                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
799                                 regions.push_back (arv);
800                         }
801                 }
802
803                 if (!regions.empty()) {
804                         selection->add (regions);
805                         commit = true;
806                 }
807         }
808
809   out:
810         return commit;
811 }
812
813
814 void
815 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
816 {
817         vector<RegionView*> all_equivalent_regions;
818
819         get_regions_corresponding_to (region, all_equivalent_regions);
820
821         if (all_equivalent_regions.empty()) {
822                 return;
823         }
824
825         begin_reversible_command (_("set selected regions"));
826
827         switch (op) {
828         case Selection::Toggle:
829                 /* XXX this is not correct */
830                 selection->toggle (all_equivalent_regions);
831                 break;
832         case Selection::Set:
833                 selection->set (all_equivalent_regions);
834                 break;
835         case Selection::Extend:
836                 selection->add (all_equivalent_regions);
837                 break;
838         case Selection::Add:
839                 selection->add (all_equivalent_regions);
840                 break;
841         }
842
843         commit_reversible_command () ;
844 }
845
846 bool
847 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
848 {
849         RegionView* rv;
850         boost::shared_ptr<Region> r (weak_r.lock());
851
852         if (!r) {
853                 return true;
854         }
855
856         if ((rv = sv->find_view (r)) == 0) {
857                 return true;
858         }
859
860         /* don't reset the selection if its something other than
861            a single other region.
862         */
863
864         if (selection->regions.size() > 1) {
865                 return true;
866         }
867
868         begin_reversible_command (_("set selected regions"));
869
870         selection->set (rv);
871
872         commit_reversible_command () ;
873
874         return true;
875 }
876
877 void
878 Editor::track_selection_changed ()
879 {
880         switch (selection->tracks.size()) {
881         case 0:
882                 break;
883         default:
884                 set_selected_mixer_strip (*(selection->tracks.front()));
885                 break;
886         }
887
888         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
889
890                 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
891                 
892                 (*i)->set_selected (yn);
893                 
894                 TimeAxisView::Children c = (*i)->get_child_list ();
895                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
896                         (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
897                 }
898
899                 if (yn && 
900                     ((mouse_mode == MouseRange) || 
901                      ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
902                         (*i)->reshow_selection (selection->time);
903                 } else {
904                         (*i)->hide_selection ();
905                 }
906         }
907
908         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
909 }
910
911 void
912 Editor::time_selection_changed ()
913 {
914         if (Profile->get_sae()) {
915                 return;
916         }
917
918         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
919                 (*i)->hide_selection ();
920         }
921
922         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
923                 (*i)->show_selection (selection->time);
924         }
925
926         if (selection->time.empty()) {
927                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
928         } else {
929                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
930         }
931 }
932
933 /** Set all region actions to have a given sensitivity */
934 void
935 Editor::sensitize_all_region_actions (bool s)
936 {
937         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
938
939         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
940                 (*i)->set_sensitive (s);
941         }
942
943         _all_region_actions_sensitized = s;
944 }
945
946 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
947  *  This method should be called just before displaying a Region menu.  When a Region menu is not
948  *  currently being shown, all region actions are sensitized so that hotkey-triggered actions
949  *  on entered_regionviews work without having to check sensitivity every time the selection or
950  *  entered_regionview changes.
951  *
952  *  This method also sets up toggle action state as appropriate.
953  */
954 void
955 Editor::sensitize_the_right_region_actions ()
956 {
957         if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
958                 sensitize_all_region_actions (false);
959                 if (!selection->time.empty()) {
960                         _region_actions->get_action("split-region")->set_sensitive (true);
961                 }
962                 
963                 return;
964
965         } else if (mouse_mode != MouseObject) {
966                 sensitize_all_region_actions (false);
967                 return;
968         }
969
970         /* We get here if we are in Object mode */
971                         
972         RegionSelection rs = get_regions_from_selection_and_entered ();
973         sensitize_all_region_actions (!rs.empty ());
974
975         _ignore_region_action = true;
976         
977         /* Look through the regions that are selected and make notes about what we have got */
978         
979         bool have_audio = false;
980         bool have_midi = false;
981         bool have_locked = false;
982         bool have_unlocked = false;
983         bool have_position_lock_style_audio = false;
984         bool have_position_lock_style_music = false;
985         bool have_muted = false;
986         bool have_unmuted = false;
987         bool have_opaque = false;
988         bool have_non_opaque = false;
989         bool have_not_at_natural_position = false;
990         bool have_envelope_visible = false;
991         bool have_envelope_invisible = false;
992         bool have_envelope_active = false;
993         bool have_envelope_inactive = false;
994         bool have_non_unity_scale_amplitude = false;
995
996         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
997
998                 boost::shared_ptr<Region> r = (*i)->region ();
999                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1000                 
1001                 if (ar) {
1002                         have_audio = true;
1003                 }
1004                 
1005                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1006                         have_midi = true;
1007                 }
1008
1009                 if (r->locked()) {
1010                         have_locked = true;
1011                 } else {
1012                         have_unlocked = true;
1013                 }
1014
1015                 if (r->position_lock_style() == MusicTime) {
1016                         have_position_lock_style_music = true;
1017                 } else {
1018                         have_position_lock_style_audio = true;
1019                 }
1020
1021                 if (r->muted()) {
1022                         have_muted = true;
1023                 } else {
1024                         have_unmuted = true;
1025                 }
1026
1027                 if (r->opaque()) {
1028                         have_opaque = true;
1029                 } else {
1030                         have_non_opaque = true;
1031                 }
1032
1033                 if (!r->at_natural_position()) {
1034                         have_not_at_natural_position = true;
1035                 }
1036
1037                 if (ar) {
1038                         /* its a bit unfortunate that "envelope visible" is a view-only
1039                            property. we have to find the regionview to able to check
1040                            its current setting.
1041                         */
1042
1043                         have_envelope_invisible = true;
1044
1045                         if (*i) {
1046                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1047                                 if (arv && arv->envelope_visible()) {
1048                                         have_envelope_visible = true;
1049                                 }
1050                         }
1051
1052                         if (ar->envelope_active()) {
1053                                 have_envelope_active = true;
1054                         } else {
1055                                 have_envelope_inactive = true;
1056                         }
1057
1058                         if (ar->scale_amplitude() != 1) {
1059                                 have_non_unity_scale_amplitude = true;
1060                         }
1061                 }
1062         }
1063
1064         if (rs.size() > 1) {
1065                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1066                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1067                 _region_actions->get_action("rename-region")->set_sensitive (false);
1068         } else if (rs.size() == 1) {
1069                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1070                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1071         } 
1072
1073         if (!have_midi) {
1074                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1075                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1076                 _region_actions->get_action("fork-region")->set_sensitive (false);
1077         }
1078
1079         if (_edit_point == EditAtMouse) {
1080                 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1081                 _region_actions->get_action("trim-front")->set_sensitive (false);
1082                 _region_actions->get_action("trim-back")->set_sensitive (false);
1083                 _region_actions->get_action("split-region")->set_sensitive (false);
1084                 _region_actions->get_action("place-transient")->set_sensitive (false);
1085         }
1086
1087         if (have_audio) {
1088                 
1089                 if (have_envelope_visible && !have_envelope_invisible) {
1090                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1091                 } else if (have_envelope_visible && have_envelope_invisible) {
1092 //                      _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1093                 }
1094                 
1095                 if (have_envelope_active && !have_envelope_inactive) {
1096                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1097                 } else if (have_envelope_active && have_envelope_inactive) {
1098 //                      _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1099                 }
1100         
1101         } else {
1102                 
1103                 _region_actions->get_action("analyze-region")->set_sensitive (false);
1104                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1105                 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1106                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1107                 
1108         }
1109
1110         if (!have_non_unity_scale_amplitude || !have_audio) {
1111                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1112         }
1113                 
1114         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1115         if (have_locked && have_unlocked) {
1116 //              _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1117         }
1118
1119         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);
1120                 
1121         if (have_position_lock_style_music && have_position_lock_style_audio) {
1122 //              _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1123         }
1124
1125         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1126         if (have_muted && have_unmuted) {
1127 //              _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1128         }
1129         
1130         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1131         if (have_opaque && have_non_opaque) {
1132 //                      _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1133         }
1134
1135         if (!have_not_at_natural_position) {
1136                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1137         }
1138
1139         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1140         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1141                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1142         } else {
1143                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1144         }
1145
1146         _ignore_region_action = false;
1147         
1148         _all_region_actions_sensitized = false;
1149 }
1150
1151
1152 void
1153 Editor::region_selection_changed ()
1154 {
1155         _regions->block_change_connection (true);
1156         editor_regions_selection_changed_connection.block(true);
1157
1158         if (_region_selection_change_updates_region_list) {
1159                 _regions->unselect_all ();
1160         }
1161
1162         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1163                 (*i)->set_selected_regionviews (selection->regions);
1164         }
1165
1166         if (_region_selection_change_updates_region_list) {
1167                 _regions->set_selected (selection->regions);
1168         }
1169
1170         _regions->block_change_connection (false);
1171         editor_regions_selection_changed_connection.block(false);
1172
1173         if (!_all_region_actions_sensitized) {
1174                 /* This selection change might have changed what region actions
1175                    are allowed, so sensitize them all in case a key is pressed.
1176                 */
1177                 sensitize_all_region_actions (true);
1178         }
1179 }
1180
1181 void
1182 Editor::point_selection_changed ()
1183 {
1184         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1185                 (*i)->set_selected_points (selection->points);
1186         }
1187 }
1188
1189 void
1190 Editor::select_all_in_track (Selection::Operation op)
1191 {
1192         list<Selectable *> touched;
1193
1194         if (!clicked_routeview) {
1195                 return;
1196         }
1197
1198         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1199
1200         switch (op) {
1201         case Selection::Toggle:
1202                 selection->add (touched);
1203                 break;
1204         case Selection::Set:
1205                 selection->set (touched);
1206                 break;
1207         case Selection::Extend:
1208                 /* meaningless, because we're selecting everything */
1209                 break;
1210         case Selection::Add:
1211                 selection->add (touched);
1212                 break;
1213         }
1214 }
1215
1216 void
1217 Editor::select_all_internal_edit (Selection::Operation op)
1218 {
1219         /* currently limited to MIDI only */
1220
1221         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1222                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1223                 if (mrv) {
1224                         mrv->select_all_notes ();
1225                 }
1226         }
1227 }
1228
1229 void
1230 Editor::select_all (Selection::Operation op)
1231 {
1232         list<Selectable *> touched;
1233
1234         if (_internal_editing) {
1235                 select_all_internal_edit (op);
1236                 return;
1237         }
1238
1239         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1240                 if ((*iter)->hidden()) {
1241                         continue;
1242                 }
1243                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1244         }
1245         begin_reversible_command (_("select all"));
1246         switch (op) {
1247         case Selection::Add:
1248                 selection->add (touched);
1249                 break;
1250         case Selection::Toggle:
1251                 selection->add (touched);
1252                 break;
1253         case Selection::Set:
1254                 selection->set (touched);
1255                 break;
1256         case Selection::Extend:
1257                 /* meaningless, because we're selecting everything */
1258                 break;
1259         }
1260         commit_reversible_command ();
1261 }
1262 void
1263 Editor::invert_selection_in_track ()
1264 {
1265         list<Selectable *> touched;
1266
1267         if (!clicked_routeview) {
1268                 return;
1269         }
1270
1271         clicked_routeview->get_inverted_selectables (*selection, touched);
1272         selection->set (touched);
1273 }
1274
1275 void
1276 Editor::invert_selection ()
1277 {
1278         list<Selectable *> touched;
1279
1280         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1281                 if ((*iter)->hidden()) {
1282                         continue;
1283                 }
1284                 (*iter)->get_inverted_selectables (*selection, touched);
1285         }
1286
1287         selection->set (touched);
1288 }
1289
1290 /** @param start Start time in session frames.
1291  *  @param end End time in session frames.
1292  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1293  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1294  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1295  *  within the region are already selected.
1296  */
1297 void
1298 Editor::select_all_within (
1299         framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected
1300         )
1301 {
1302         list<Selectable*> found;
1303
1304         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1305                 
1306                 if ((*iter)->hidden()) {
1307                         continue;
1308                 }
1309
1310                 (*iter)->get_selectables (start, end, top, bot, found);
1311         }
1312
1313         if (found.empty()) {
1314                 return;
1315         }
1316
1317         if (preserve_if_selected && op != Selection::Toggle) {
1318                 list<Selectable*>::iterator i = found.begin();
1319                 while (i != found.end() && (*i)->get_selected()) {
1320                         ++i;
1321                 }
1322
1323                 if (i == found.end()) {
1324                         return;
1325                 }
1326         }
1327
1328         begin_reversible_command (_("select all within"));
1329         switch (op) {
1330         case Selection::Add:
1331                 selection->add (found);
1332                 break;
1333         case Selection::Toggle:
1334                 selection->toggle (found);
1335                 break;
1336         case Selection::Set:
1337                 selection->set (found);
1338                 break;
1339         case Selection::Extend:
1340                 /* not defined yet */
1341                 break;
1342         }
1343
1344         commit_reversible_command ();
1345 }
1346
1347 void
1348 Editor::set_selection_from_region ()
1349 {
1350         if (selection->regions.empty()) {
1351                 return;
1352         }
1353
1354         selection->set (selection->regions.start(), selection->regions.end_frame());
1355         if (!Profile->get_sae()) {
1356                 set_mouse_mode (Editing::MouseRange, false);
1357         }
1358 }
1359
1360 void
1361 Editor::set_selection_from_punch()
1362 {
1363         Location* location;
1364
1365         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1366                 return;
1367         }
1368
1369         set_selection_from_range (*location);
1370 }
1371
1372 void
1373 Editor::set_selection_from_loop()
1374 {
1375         Location* location;
1376
1377         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1378                 return;
1379         }
1380         set_selection_from_range (*location);
1381 }
1382
1383 void
1384 Editor::set_selection_from_range (Location& loc)
1385 {
1386         begin_reversible_command (_("set selection from range"));
1387         selection->set (loc.start(), loc.end());
1388         commit_reversible_command ();
1389
1390         if (!Profile->get_sae()) {
1391                 set_mouse_mode (Editing::MouseRange, false);
1392         }
1393 }
1394
1395 void
1396 Editor::select_all_selectables_using_time_selection ()
1397 {
1398         list<Selectable *> touched;
1399
1400         if (selection->time.empty()) {
1401                 return;
1402         }
1403
1404         framepos_t start = selection->time[clicked_selection].start;
1405         framepos_t end = selection->time[clicked_selection].end;
1406
1407         if (end - start < 1)  {
1408                 return;
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 (start, end - 1, 0, DBL_MAX, touched);
1424         }
1425
1426         begin_reversible_command (_("select all from range"));
1427         selection->set (touched);
1428         commit_reversible_command ();
1429 }
1430
1431
1432 void
1433 Editor::select_all_selectables_using_punch()
1434 {
1435         Location* location = _session->locations()->auto_punch_location();
1436         list<Selectable *> touched;
1437
1438         if (location == 0 || (location->end() - location->start() <= 1))  {
1439                 return;
1440         }
1441
1442
1443         TrackViewList* ts;
1444
1445         if (selection->tracks.empty()) {
1446                 ts = &track_views;
1447         } else {
1448                 ts = &selection->tracks;
1449         }
1450
1451         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1452                 if ((*iter)->hidden()) {
1453                         continue;
1454                 }
1455                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1456         }
1457         begin_reversible_command (_("select all from punch"));
1458         selection->set (touched);
1459         commit_reversible_command ();
1460
1461 }
1462
1463 void
1464 Editor::select_all_selectables_using_loop()
1465 {
1466         Location* location = _session->locations()->auto_loop_location();
1467         list<Selectable *> touched;
1468
1469         if (location == 0 || (location->end() - location->start() <= 1))  {
1470                 return;
1471         }
1472
1473
1474         TrackViewList* ts;
1475
1476         if (selection->tracks.empty()) {
1477                 ts = &track_views;
1478         } else {
1479                 ts = &selection->tracks;
1480         }
1481
1482         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1483                 if ((*iter)->hidden()) {
1484                         continue;
1485                 }
1486                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1487         }
1488         begin_reversible_command (_("select all from loop"));
1489         selection->set (touched);
1490         commit_reversible_command ();
1491
1492 }
1493
1494 void
1495 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1496 {
1497         framepos_t start;
1498         framepos_t end;
1499         list<Selectable *> touched;
1500
1501         if (after) {
1502                 begin_reversible_command (_("select all after cursor"));
1503                 start = cursor->current_frame;
1504                 end = _session->current_end_frame();
1505         } else {
1506                 if (cursor->current_frame > 0) {
1507                         begin_reversible_command (_("select all before cursor"));
1508                         start = 0;
1509                         end = cursor->current_frame - 1;
1510                 } else {
1511                         return;
1512                 }
1513         }
1514
1515
1516         TrackViewList* ts;
1517
1518         if (selection->tracks.empty()) {
1519                 ts = &track_views;
1520         } else {
1521                 ts = &selection->tracks;
1522         }
1523
1524         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1525                 if ((*iter)->hidden()) {
1526                         continue;
1527                 }
1528                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1529         }
1530         selection->set (touched);
1531         commit_reversible_command ();
1532 }
1533
1534 void
1535 Editor::select_all_selectables_using_edit (bool after)
1536 {
1537         framepos_t start;
1538         framepos_t end;
1539         list<Selectable *> touched;
1540
1541         if (after) {
1542                 begin_reversible_command (_("select all after edit"));
1543                 start = get_preferred_edit_position();
1544                 end = _session->current_end_frame();
1545         } else {
1546                 if ((end = get_preferred_edit_position()) > 1) {
1547                         begin_reversible_command (_("select all before edit"));
1548                         start = 0;
1549                         end -= 1;
1550                 } else {
1551                         return;
1552                 }
1553         }
1554
1555
1556         TrackViewList* ts;
1557
1558         if (selection->tracks.empty()) {
1559                 ts = &track_views;
1560         } else {
1561                 ts = &selection->tracks;
1562         }
1563
1564         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1565                 if ((*iter)->hidden()) {
1566                         continue;
1567                 }
1568                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1569         }
1570         selection->set (touched);
1571         commit_reversible_command ();
1572 }
1573
1574 void
1575 Editor::select_all_selectables_between (bool /*within*/)
1576 {
1577         framepos_t start;
1578         framepos_t end;
1579         list<Selectable *> touched;
1580
1581         if (!get_edit_op_range (start, end)) {
1582                 return;
1583         }
1584
1585         TrackViewList* ts;
1586
1587         if (selection->tracks.empty()) {
1588                 ts = &track_views;
1589         } else {
1590                 ts = &selection->tracks;
1591         }
1592
1593         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1594                 if ((*iter)->hidden()) {
1595                         continue;
1596                 }
1597                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1598         }
1599
1600         selection->set (touched);
1601 }
1602
1603 void
1604 Editor::select_range_between ()
1605 {
1606         framepos_t start;
1607         framepos_t end;
1608
1609         if (mouse_mode == MouseRange && !selection->time.empty()) {
1610                 selection->clear_time ();
1611         }
1612
1613         if (!get_edit_op_range (start, end)) {
1614                 return;
1615         }
1616
1617         set_mouse_mode (MouseRange);
1618         selection->set (start, end);
1619 }
1620
1621 bool
1622 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1623 {
1624         framepos_t m;
1625         bool ignored;
1626
1627         /* in range mode, use any existing selection */
1628
1629         if (mouse_mode == MouseRange && !selection->time.empty()) {
1630                 /* we know that these are ordered */
1631                 start = selection->time.start();
1632                 end = selection->time.end_frame();
1633                 return true;
1634         }
1635
1636         if (!mouse_frame (m, ignored)) {
1637                 /* mouse is not in a canvas, try playhead+selected marker.
1638                    this is probably most true when using menus.
1639                  */
1640
1641                 if (selection->markers.empty()) {
1642                         return false;
1643                 }
1644
1645                 start = selection->markers.front()->position();
1646                 end = _session->audible_frame();
1647
1648         } else {
1649
1650                 switch (_edit_point) {
1651                 case EditAtPlayhead:
1652                         if (selection->markers.empty()) {
1653                                 /* use mouse + playhead */
1654                                 start = m;
1655                                 end = _session->audible_frame();
1656                         } else {
1657                                 /* use playhead + selected marker */
1658                                 start = _session->audible_frame();
1659                                 end = selection->markers.front()->position();
1660                         }
1661                         break;
1662
1663                 case EditAtMouse:
1664                         /* use mouse + selected marker */
1665                         if (selection->markers.empty()) {
1666                                 start = m;
1667                                 end = _session->audible_frame();
1668                         } else {
1669                                 start = selection->markers.front()->position();
1670                                 end = m;
1671                         }
1672                         break;
1673
1674                 case EditAtSelectedMarker:
1675                         /* use mouse + selected marker */
1676                         if (selection->markers.empty()) {
1677
1678                                 MessageDialog win (_("No edit range defined"),
1679                                                    false,
1680                                                    MESSAGE_INFO,
1681                                                    BUTTONS_OK);
1682
1683                                 win.set_secondary_text (
1684                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1685
1686
1687                                 win.set_default_response (RESPONSE_CLOSE);
1688                                 win.set_position (Gtk::WIN_POS_MOUSE);
1689                                 win.show_all();
1690
1691                                 win.run ();
1692
1693                                 return false; // NO RANGE
1694                         }
1695                         start = selection->markers.front()->position();
1696                         end = m;
1697                         break;
1698                 }
1699         }
1700
1701         if (start == end) {
1702                 return false;
1703         }
1704
1705         if (start > end) {
1706                 swap (start, end);
1707         }
1708
1709         /* turn range into one delimited by start...end,
1710            not start...end-1
1711         */
1712
1713         end++;
1714
1715         return true;
1716 }
1717
1718 void
1719 Editor::deselect_all ()
1720 {
1721         selection->clear ();
1722 }
1723
1724 long
1725 Editor::select_range_around_region (RegionView* rv)
1726 {
1727         assert (rv);
1728         
1729         selection->set (&rv->get_time_axis_view());
1730         
1731         selection->time.clear ();
1732         boost::shared_ptr<Region> r = rv->region ();
1733         return selection->set (r->position(), r->position() + r->length());
1734 }