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