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