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