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