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