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