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