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