Remove Cruft
[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         for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1009                 (*i)->set_selected (false);
1010                 (*i)->hide_selection ();
1011         }
1012
1013         /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1014          * currently selected stripable/controllable duples are found and added
1015          */
1016
1017         selection->core_selection_changed (what_changed);
1018
1019         /* STEP 2: update TimeAxisView's knowledge of their selected state
1020          */
1021
1022         if (what_changed.contains (Properties::selected)) {
1023
1024                 StripableNotificationListPtr stripables (new StripableNotificationList);
1025
1026                 switch (selection->tracks.size()) {
1027                 case 0:
1028                         break;
1029                 default:
1030                         set_selected_mixer_strip (*(selection->tracks.back()));
1031                         if (!_track_selection_change_without_scroll) {
1032                                 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1033                         }
1034                         break;
1035                 }
1036
1037                 CoreSelection::StripableAutomationControls sc;
1038                 _session->selection().get_stripables (sc);
1039
1040                 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1041
1042                         AxisView* av = axis_view_by_stripable ((*i).stripable);
1043
1044                         if (!av) {
1045                                 continue;
1046                         }
1047
1048                         TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1049
1050                         if (!tav) {
1051                                 continue; /* impossible */
1052                         }
1053
1054                         if (!(*i).controllable) {
1055
1056                                 /* "parent" track selected */
1057                                 tav->set_selected (true);
1058                                 tav->reshow_selection (selection->time);
1059
1060                         } else {
1061
1062                                 /* possibly a child */
1063
1064                                 TimeAxisView::Children c = tav->get_child_list ();
1065
1066                                 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1067
1068                                         boost::shared_ptr<AutomationControl> control = (*j)->control ();
1069
1070                                         if (control != (*i).controllable) {
1071                                                 continue;
1072                                         }
1073
1074                                         (*j)->set_selected (true);
1075                                         (*j)->reshow_selection (selection->time);
1076                                 }
1077                         }
1078
1079                         stripables->push_back ((*i).stripable);
1080                 }
1081
1082                 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1083
1084                 sensitize_the_right_region_actions (false);
1085
1086                 /* STEP 4: notify control protocols */
1087
1088                 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1089
1090                 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1091                         uint32_t audio_track_cnt = 0;
1092                         uint32_t midi_track_cnt = 0;
1093
1094                         for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1095                                 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1096
1097                                 if (atv) {
1098                                         if (atv->is_audio_track()) {
1099                                                 audio_track_cnt++;
1100                                         }
1101
1102                                 } else {
1103                                         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1104
1105                                         if (mtv) {
1106                                                 if (mtv->is_midi_track()) {
1107                                                         midi_track_cnt++;
1108                                                 }
1109                                         }
1110                                 }
1111                         }
1112
1113                         sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1114                 }
1115         }
1116
1117         /* STEP 4: update EditorRoutes treeview */
1118
1119         PropertyChange soh;
1120
1121         soh.add (Properties::selected);
1122         soh.add (Properties::order);
1123         soh.add (Properties::hidden);
1124
1125         if (what_changed.contains (soh)) {
1126                 _routes->sync_treeview_from_presentation_info (what_changed);
1127         }
1128 }
1129
1130 void
1131 Editor::time_selection_changed ()
1132 {
1133         /* XXX this is superficially inefficient. Hide the selection in all
1134          * tracks, then show it in all selected tracks.
1135          *
1136          * However, if you investigate what this actually does, it isn't
1137          * anywhere nearly as bad as it may appear. Remember: nothing is
1138          * redrawn or even recomputed during these two loops - that only
1139          * happens when we next render ...
1140          */
1141
1142         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1143                 (*i)->hide_selection ();
1144         }
1145
1146         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1147                 (*i)->show_selection (selection->time);
1148         }
1149
1150         if (selection->time.empty()) {
1151                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1152         } else {
1153                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1154         }
1155
1156         /* propagate into backend, but only when there is no drag or we are at
1157          * the end of a drag, otherwise this is too expensive (could case a
1158          * locate per mouse motion event.
1159          */
1160
1161         if (_session && !_drags->active()) {
1162                 if (selection->time.length() != 0) {
1163                         _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1164                 } else {
1165                         _session->clear_range_selection ();
1166                 }
1167         }
1168 }
1169
1170 /** Set all region actions to have a given sensitivity */
1171 void
1172 Editor::sensitize_all_region_actions (bool s)
1173 {
1174         Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1175
1176         for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1177                 (*i)->set_sensitive (s);
1178         }
1179
1180         _all_region_actions_sensitized = s;
1181 }
1182
1183 /** Sensitize region-based actions.
1184  *
1185  *  This method is called from whenever we leave the canvas, either by moving
1186  *  the pointer out of it, or by popping up a context menu. See
1187  *  Editor::{entered,left}_track_canvas() for details there.
1188  */
1189 void
1190 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1191 {
1192         bool have_selection = false;
1193         bool have_entered = false;
1194         bool have_edit_point = false;
1195         RegionSelection rs;
1196
1197         // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1198         // << std::endl;
1199
1200         if (!selection->regions.empty()) {
1201                 have_selection = true;
1202                 rs = selection->regions;
1203         }
1204
1205         if (entered_regionview) {
1206                 have_entered = true;
1207                 rs.add (entered_regionview);
1208         }
1209
1210         if (rs.empty() && !selection->tracks.empty()) {
1211
1212                 /* no selected regions, but some selected tracks.
1213                  */
1214
1215                 if (_edit_point == EditAtMouse) {
1216                         if (!within_track_canvas) {
1217                                 /* pointer is not in canvas, so edit point is meaningless */
1218                                 have_edit_point = false;
1219                         } else {
1220                                 /* inside canvas. we don't know where the edit
1221                                    point will be when an action is invoked, but
1222                                    assume it could intersect with a region.
1223                                 */
1224                                 have_edit_point = true;
1225                         }
1226                 } else {
1227                         RegionSelection at_edit_point;
1228                         framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1229                         get_regions_at (at_edit_point, where, selection->tracks);
1230                         if (!at_edit_point.empty()) {
1231                                 have_edit_point = true;
1232                         }
1233                         if (rs.empty()) {
1234                                 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1235                         }
1236                 }
1237         }
1238
1239         //std::cerr << "\tfinal have selection: " << have_selection
1240         // << " have entered " << have_entered
1241         // << " have edit point " << have_edit_point
1242         // << " EP = " << enum_2_string (_edit_point)
1243         // << std::endl;
1244
1245         typedef std::map<std::string,RegionAction> RegionActionMap;
1246
1247         _ignore_region_action = true;
1248
1249         for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1250                 RegionActionTarget tgt = x->second.target;
1251                 bool sensitive = false;
1252
1253                 if ((tgt & SelectedRegions) && have_selection) {
1254                         sensitive = true;
1255                 } else if ((tgt & EnteredRegions) && have_entered) {
1256                         sensitive = true;
1257                 } else if ((tgt & EditPointRegions) && have_edit_point) {
1258                         sensitive = true;
1259                 }
1260
1261                 x->second.action->set_sensitive (sensitive);
1262         }
1263
1264         /* Look through the regions that are selected and make notes about what we have got */
1265
1266         bool have_audio = false;
1267         bool have_multichannel_audio = false;
1268         bool have_midi = false;
1269         bool have_locked = false;
1270         bool have_unlocked = false;
1271         bool have_video_locked = false;
1272         bool have_video_unlocked = false;
1273         bool have_position_lock_style_audio = false;
1274         bool have_position_lock_style_music = false;
1275         bool have_muted = false;
1276         bool have_unmuted = false;
1277         bool have_opaque = false;
1278         bool have_non_opaque = false;
1279         bool have_not_at_natural_position = false;
1280         bool have_envelope_active = false;
1281         bool have_envelope_inactive = false;
1282         bool have_non_unity_scale_amplitude = false;
1283         bool have_compound_regions = false;
1284         bool have_inactive_fade_in = false;
1285         bool have_inactive_fade_out = false;
1286         bool have_active_fade_in = false;
1287         bool have_active_fade_out = false;
1288         bool have_transients = false;
1289
1290         for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1291
1292                 boost::shared_ptr<Region> r = (*i)->region ();
1293                 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1294
1295                 if (ar) {
1296                         have_audio = true;
1297                         if (ar->n_channels() > 1) {
1298                                 have_multichannel_audio = true;
1299                         }
1300                 }
1301
1302                 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1303                         have_midi = true;
1304                 }
1305
1306                 if (r->is_compound()) {
1307                         have_compound_regions = true;
1308                 }
1309
1310                 if (r->locked()) {
1311                         have_locked = true;
1312                 } else {
1313                         have_unlocked = true;
1314                 }
1315
1316                 if (r->video_locked()) {
1317                         have_video_locked = true;
1318                 } else {
1319                         have_video_unlocked = true;
1320                 }
1321
1322                 if (r->position_lock_style() == MusicTime) {
1323                         have_position_lock_style_music = true;
1324                 } else {
1325                         have_position_lock_style_audio = true;
1326                 }
1327
1328                 if (r->muted()) {
1329                         have_muted = true;
1330                 } else {
1331                         have_unmuted = true;
1332                 }
1333
1334                 if (r->opaque()) {
1335                         have_opaque = true;
1336                 } else {
1337                         have_non_opaque = true;
1338                 }
1339
1340                 if (!r->at_natural_position()) {
1341                         have_not_at_natural_position = true;
1342                 }
1343
1344                 if (r->has_transients ()){
1345                         have_transients = true;
1346                 }
1347
1348                 if (ar) {
1349                         if (ar->envelope_active()) {
1350                                 have_envelope_active = true;
1351                         } else {
1352                                 have_envelope_inactive = true;
1353                         }
1354
1355                         if (ar->scale_amplitude() != 1) {
1356                                 have_non_unity_scale_amplitude = true;
1357                         }
1358
1359                         if (ar->fade_in_active ()) {
1360                                 have_active_fade_in = true;
1361                         } else {
1362                                 have_inactive_fade_in = true;
1363                         }
1364
1365                         if (ar->fade_out_active ()) {
1366                                 have_active_fade_out = true;
1367                         } else {
1368                                 have_inactive_fade_out = true;
1369                         }
1370                 }
1371         }
1372
1373         _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1374
1375         if (rs.size() > 1) {
1376                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1377                 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1378                 _region_actions->get_action("rename-region")->set_sensitive (false);
1379                 if (have_audio) {
1380                         /* XXX need to check whether there is than 1 per
1381                            playlist, because otherwise this makes no sense.
1382                         */
1383                         _region_actions->get_action("combine-regions")->set_sensitive (true);
1384                 } else {
1385                         _region_actions->get_action("combine-regions")->set_sensitive (false);
1386                 }
1387         } else if (rs.size() == 1) {
1388                 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1389                 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1390                 _region_actions->get_action("combine-regions")->set_sensitive (false);
1391         }
1392
1393         if (!have_multichannel_audio) {
1394                 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1395         }
1396
1397         if (!have_midi) {
1398                 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1399                 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1400                 _region_actions->get_action("quantize-region")->set_sensitive (false);
1401                 _region_actions->get_action("legatize-region")->set_sensitive (false);
1402                 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1403                 _region_actions->get_action("transform-region")->set_sensitive (false);
1404                 _region_actions->get_action("fork-region")->set_sensitive (false);
1405                 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1406                 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1407                 _region_actions->get_action("transpose-region")->set_sensitive (false);
1408         } else {
1409                 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1410                 /* others were already marked sensitive */
1411         }
1412
1413         /* ok, moving along... */
1414
1415         if (have_compound_regions) {
1416                 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1417         } else {
1418                 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1419         }
1420
1421         if (have_audio) {
1422
1423                 if (have_envelope_active && !have_envelope_inactive) {
1424                         Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1425                 } else if (have_envelope_active && have_envelope_inactive) {
1426                         // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1427                 }
1428
1429         } else {
1430
1431                 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1432                 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1433                 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1434                 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1435                 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1436                 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1437                 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1438
1439         }
1440
1441         if (!have_non_unity_scale_amplitude || !have_audio) {
1442                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1443         }
1444
1445         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1446         a->set_active (have_locked && !have_unlocked);
1447         if (have_locked && have_unlocked) {
1448                 // a->set_inconsistent ();
1449         }
1450
1451         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1452         a->set_active (have_video_locked && !have_video_unlocked);
1453         if (have_video_locked && have_video_unlocked) {
1454                 // a->set_inconsistent ();
1455         }
1456
1457         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1458         a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1459
1460         vector<Widget*> proxies = a->get_proxies();
1461         for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1462                 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1463                 if (cmi) {
1464                         cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1465                 }
1466         }
1467
1468         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1469         a->set_active (have_muted && !have_unmuted);
1470         if (have_muted && have_unmuted) {
1471                 // a->set_inconsistent ();
1472         }
1473
1474         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1475         a->set_active (have_opaque && !have_non_opaque);
1476         if (have_opaque && have_non_opaque) {
1477                 // a->set_inconsistent ();
1478         }
1479
1480         if (!have_not_at_natural_position) {
1481                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1482         }
1483
1484         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1485         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1486                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1487         } else {
1488                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1489         }
1490
1491         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1492         a->set_active (have_active_fade_in && !have_inactive_fade_in);
1493         if (have_active_fade_in && have_inactive_fade_in) {
1494                 // a->set_inconsistent ();
1495         }
1496
1497         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1498         a->set_active (have_active_fade_out && !have_inactive_fade_out);
1499
1500         if (have_active_fade_out && have_inactive_fade_out) {
1501                 // a->set_inconsistent ();
1502         }
1503
1504         bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1505         bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1506
1507         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1508         a->set_active (have_active_fade && !have_inactive_fade);
1509
1510         if (have_active_fade && have_inactive_fade) {
1511                 // a->set_inconsistent ();
1512         }
1513
1514         _ignore_region_action = false;
1515
1516         _all_region_actions_sensitized = false;
1517 }
1518
1519 void
1520 Editor::region_selection_changed ()
1521 {
1522         _regions->block_change_connection (true);
1523         editor_regions_selection_changed_connection.block(true);
1524
1525         if (_region_selection_change_updates_region_list) {
1526                 _regions->unselect_all ();
1527         }
1528
1529         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1530                 (*i)->set_selected_regionviews (selection->regions);
1531         }
1532
1533         if (_region_selection_change_updates_region_list) {
1534                 _regions->set_selected (selection->regions);
1535         }
1536
1537         _regions->block_change_connection (false);
1538         editor_regions_selection_changed_connection.block(false);
1539
1540         sensitize_the_right_region_actions (false);
1541
1542         /* propagate into backend */
1543
1544         if (_session) {
1545                 if (!selection->regions.empty()) {
1546                         _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1547                 } else {
1548                         _session->clear_object_selection ();
1549                 }
1550         }
1551
1552 }
1553
1554 void
1555 Editor::point_selection_changed ()
1556 {
1557         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1558                 (*i)->set_selected_points (selection->points);
1559         }
1560 }
1561
1562 void
1563 Editor::select_all_in_track (Selection::Operation op)
1564 {
1565         list<Selectable *> touched;
1566
1567         if (!clicked_routeview) {
1568                 return;
1569         }
1570
1571         begin_reversible_selection_op (X_("Select All in Track"));
1572
1573         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1574
1575         switch (op) {
1576         case Selection::Toggle:
1577                 selection->add (touched);
1578                 break;
1579         case Selection::Set:
1580                 selection->set (touched);
1581                 break;
1582         case Selection::Extend:
1583                 /* meaningless, because we're selecting everything */
1584                 break;
1585         case Selection::Add:
1586                 selection->add (touched);
1587                 break;
1588         }
1589
1590         commit_reversible_selection_op ();
1591 }
1592
1593 bool
1594 Editor::select_all_internal_edit (Selection::Operation)
1595 {
1596         bool selected = false;
1597
1598         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1599                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1600                 if (mrv) {
1601                         mrv->select_all_notes ();
1602                         selected = true;
1603                 }
1604         }
1605
1606         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1607         if (mrv) {
1608                 mrv->select_all_notes ();
1609                 selected = true;
1610         }
1611
1612         return selected;
1613 }
1614
1615 void
1616 Editor::select_all_objects (Selection::Operation op)
1617 {
1618         list<Selectable *> touched;
1619
1620         if (internal_editing() && select_all_internal_edit(op)) {
1621                 return;  // Selected notes
1622         }
1623
1624         TrackViewList ts;
1625
1626         if (selection->tracks.empty()) {
1627                 ts = track_views;
1628         } else {
1629                 ts = selection->tracks;
1630         }
1631
1632         for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1633                 if ((*iter)->hidden()) {
1634                         continue;
1635                 }
1636                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1637         }
1638
1639         begin_reversible_selection_op (X_("select all"));
1640         switch (op) {
1641         case Selection::Add:
1642                 selection->add (touched);
1643                 break;
1644         case Selection::Toggle:
1645                 selection->toggle (touched);
1646                 break;
1647         case Selection::Set:
1648                 selection->set (touched);
1649                 break;
1650         case Selection::Extend:
1651                 /* meaningless, because we're selecting everything */
1652                 break;
1653         }
1654         commit_reversible_selection_op ();
1655 }
1656
1657 void
1658 Editor::invert_selection_in_track ()
1659 {
1660         list<Selectable *> touched;
1661
1662         if (!clicked_routeview) {
1663                 return;
1664         }
1665
1666         begin_reversible_selection_op (X_("Invert Selection in Track"));
1667         clicked_routeview->get_inverted_selectables (*selection, touched);
1668         selection->set (touched);
1669         commit_reversible_selection_op ();
1670 }
1671
1672 void
1673 Editor::invert_selection ()
1674 {
1675         list<Selectable *> touched;
1676
1677         if (internal_editing()) {
1678                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1679                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1680                         if (mrv) {
1681                                 mrv->invert_selection ();
1682                         }
1683                 }
1684                 return;
1685         }
1686
1687         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1688                 if ((*iter)->hidden()) {
1689                         continue;
1690                 }
1691                 (*iter)->get_inverted_selectables (*selection, touched);
1692         }
1693
1694         begin_reversible_selection_op (X_("Invert Selection"));
1695         selection->set (touched);
1696         commit_reversible_selection_op ();
1697 }
1698
1699 /** @param start Start time in session frames.
1700  *  @param end End time in session frames.
1701  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1702  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1703  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1704  *  within the region are already selected.
1705  */
1706 void
1707 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1708 {
1709         list<Selectable*> found;
1710
1711         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1712
1713                 if ((*iter)->hidden()) {
1714                         continue;
1715                 }
1716
1717                 (*iter)->get_selectables (start, end, top, bot, found);
1718         }
1719
1720         if (found.empty()) {
1721                 selection->clear_objects();
1722                 selection->clear_time ();
1723                 return;
1724         }
1725
1726         if (preserve_if_selected && op != Selection::Toggle) {
1727                 list<Selectable*>::iterator i = found.begin();
1728                 while (i != found.end() && (*i)->selected()) {
1729                         ++i;
1730                 }
1731
1732                 if (i == found.end()) {
1733                         return;
1734                 }
1735         }
1736
1737         begin_reversible_selection_op (X_("select all within"));
1738         switch (op) {
1739         case Selection::Add:
1740                 selection->add (found);
1741                 break;
1742         case Selection::Toggle:
1743                 selection->toggle (found);
1744                 break;
1745         case Selection::Set:
1746                 selection->set (found);
1747                 break;
1748         case Selection::Extend:
1749                 /* not defined yet */
1750                 break;
1751         }
1752
1753         commit_reversible_selection_op ();
1754 }
1755
1756 void
1757 Editor::set_selection_from_region ()
1758 {
1759         if (selection->regions.empty()) {
1760                 return;
1761         }
1762
1763         /* find all the tracks that have selected regions */
1764
1765         set<TimeAxisView*> tracks;
1766
1767         for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1768                 tracks.insert (&(*r)->get_time_axis_view());
1769         }
1770
1771         TrackViewList tvl;
1772         tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1773
1774         /* select range (this will clear the region selection) */
1775
1776         selection->set (selection->regions.start(), selection->regions.end_frame());
1777
1778         /* and select the tracks */
1779
1780         selection->set (tvl);
1781
1782         if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1783                 set_mouse_mode (Editing::MouseRange, false);
1784         }
1785 }
1786
1787 void
1788 Editor::set_selection_from_punch()
1789 {
1790         Location* location;
1791
1792         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1793                 return;
1794         }
1795
1796         set_selection_from_range (*location);
1797 }
1798
1799 void
1800 Editor::set_selection_from_loop()
1801 {
1802         Location* location;
1803
1804         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1805                 return;
1806         }
1807         set_selection_from_range (*location);
1808 }
1809
1810 void
1811 Editor::set_selection_from_range (Location& loc)
1812 {
1813         begin_reversible_selection_op (X_("set selection from range"));
1814         selection->set (loc.start(), loc.end());
1815         commit_reversible_selection_op ();
1816
1817         if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1818                 set_mouse_mode (MouseRange, false);
1819         }
1820 }
1821
1822 void
1823 Editor::select_all_selectables_using_time_selection ()
1824 {
1825         list<Selectable *> touched;
1826
1827         if (selection->time.empty()) {
1828                 return;
1829         }
1830
1831         framepos_t start = selection->time[clicked_selection].start;
1832         framepos_t end = selection->time[clicked_selection].end;
1833
1834         if (end - start < 1)  {
1835                 return;
1836         }
1837
1838         TrackViewList* ts;
1839
1840         if (selection->tracks.empty()) {
1841                 ts = &track_views;
1842         } else {
1843                 ts = &selection->tracks;
1844         }
1845
1846         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1847                 if ((*iter)->hidden()) {
1848                         continue;
1849                 }
1850                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1851         }
1852
1853         begin_reversible_selection_op (X_("select all from range"));
1854         selection->set (touched);
1855         commit_reversible_selection_op ();
1856 }
1857
1858
1859 void
1860 Editor::select_all_selectables_using_punch()
1861 {
1862         Location* location = _session->locations()->auto_punch_location();
1863         list<Selectable *> touched;
1864
1865         if (location == 0 || (location->end() - location->start() <= 1))  {
1866                 return;
1867         }
1868
1869
1870         TrackViewList* ts;
1871
1872         if (selection->tracks.empty()) {
1873                 ts = &track_views;
1874         } else {
1875                 ts = &selection->tracks;
1876         }
1877
1878         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1879                 if ((*iter)->hidden()) {
1880                         continue;
1881                 }
1882                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1883         }
1884         begin_reversible_selection_op (X_("select all from punch"));
1885         selection->set (touched);
1886         commit_reversible_selection_op ();
1887
1888 }
1889
1890 void
1891 Editor::select_all_selectables_using_loop()
1892 {
1893         Location* location = _session->locations()->auto_loop_location();
1894         list<Selectable *> touched;
1895
1896         if (location == 0 || (location->end() - location->start() <= 1))  {
1897                 return;
1898         }
1899
1900
1901         TrackViewList* ts;
1902
1903         if (selection->tracks.empty()) {
1904                 ts = &track_views;
1905         } else {
1906                 ts = &selection->tracks;
1907         }
1908
1909         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1910                 if ((*iter)->hidden()) {
1911                         continue;
1912                 }
1913                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1914         }
1915         begin_reversible_selection_op (X_("select all from loop"));
1916         selection->set (touched);
1917         commit_reversible_selection_op ();
1918
1919 }
1920
1921 void
1922 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1923 {
1924         framepos_t start;
1925         framepos_t end;
1926         list<Selectable *> touched;
1927
1928         if (after) {
1929                 start = cursor->current_frame();
1930                 end = _session->current_end_frame();
1931         } else {
1932                 if (cursor->current_frame() > 0) {
1933                         start = 0;
1934                         end = cursor->current_frame() - 1;
1935                 } else {
1936                         return;
1937                 }
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                         if (mrv) {
1944                                 mrv->select_range (start, end);
1945                         }
1946                 }
1947                 return;
1948         }
1949
1950         if (after) {
1951                 begin_reversible_selection_op (X_("select all after cursor"));
1952         } else {
1953                 begin_reversible_selection_op (X_("select all before cursor"));
1954         }
1955
1956         TrackViewList* ts;
1957
1958         if (selection->tracks.empty()) {
1959                 ts = &track_views;
1960         } else {
1961                 ts = &selection->tracks;
1962         }
1963
1964         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1965                 if ((*iter)->hidden()) {
1966                         continue;
1967                 }
1968                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1969         }
1970         selection->set (touched);
1971         commit_reversible_selection_op ();
1972 }
1973
1974 void
1975 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1976 {
1977         framepos_t start;
1978         framepos_t end;
1979         list<Selectable *> touched;
1980
1981         if (after) {
1982                 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1983                 end = _session->current_end_frame();
1984         } else {
1985                 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1986                         start = 0;
1987                         end -= 1;
1988                 } else {
1989                         return;
1990                 }
1991         }
1992
1993         if (internal_editing()) {
1994                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1995                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1996                         mrv->select_range (start, end);
1997                 }
1998                 return;
1999         }
2000
2001         if (after) {
2002                 begin_reversible_selection_op (X_("select all after edit"));
2003         } else {
2004                 begin_reversible_selection_op (X_("select all before edit"));
2005         }
2006
2007         TrackViewList* ts;
2008
2009         if (selection->tracks.empty()) {
2010                 ts = &track_views;
2011         } else {
2012                 ts = &selection->tracks;
2013         }
2014
2015         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2016                 if ((*iter)->hidden()) {
2017                         continue;
2018                 }
2019                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2020         }
2021         selection->set (touched);
2022         commit_reversible_selection_op ();
2023 }
2024
2025 void
2026 Editor::select_all_selectables_between (bool within)
2027 {
2028         framepos_t start;
2029         framepos_t end;
2030         list<Selectable *> touched;
2031
2032         if (!get_edit_op_range (start, end)) {
2033                 return;
2034         }
2035
2036         if (internal_editing()) {
2037                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2038                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2039                         mrv->select_range (start, end);
2040                 }
2041                 return;
2042         }
2043
2044         TrackViewList* ts;
2045
2046         if (selection->tracks.empty()) {
2047                 ts = &track_views;
2048         } else {
2049                 ts = &selection->tracks;
2050         }
2051
2052         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2053                 if ((*iter)->hidden()) {
2054                         continue;
2055                 }
2056                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2057         }
2058
2059         begin_reversible_selection_op (X_("Select all Selectables Between"));
2060         selection->set (touched);
2061         commit_reversible_selection_op ();
2062 }
2063
2064 void
2065 Editor::select_range_between ()
2066 {
2067         framepos_t start;
2068         framepos_t end;
2069
2070         if ( !selection->time.empty() ) {
2071                 selection->clear_time ();
2072         }
2073
2074         if (!get_edit_op_range (start, end)) {
2075                 return;
2076         }
2077
2078         if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2079                 set_mouse_mode (MouseRange, false);
2080         }
2081
2082         begin_reversible_selection_op (X_("Select Range Between"));
2083         selection->set (start, end);
2084         commit_reversible_selection_op ();
2085 }
2086
2087 bool
2088 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
2089 {
2090 //      framepos_t m;
2091 //      bool ignored;
2092
2093         /* if an explicit range exists, use it */
2094
2095         if ( (mouse_mode == MouseRange || get_smart_mode() ) &&  !selection->time.empty()) {
2096                 /* we know that these are ordered */
2097                 start = selection->time.start();
2098                 end = selection->time.end_frame();
2099                 return true;
2100         } else {
2101                 start = 0;
2102                 end = 0;
2103                 return false;
2104         }
2105
2106 //      if (!mouse_frame (m, ignored)) {
2107 //              /* mouse is not in a canvas, try playhead+selected marker.
2108 //                 this is probably most true when using menus.
2109 //              */
2110 //
2111 //              if (selection->markers.empty()) {
2112 //                      return false;
2113 //              }
2114
2115 //              start = selection->markers.front()->position();
2116 //              end = _session->audible_frame();
2117
2118 //      } else {
2119
2120 //              switch (_edit_point) {
2121 //              case EditAtPlayhead:
2122 //                      if (selection->markers.empty()) {
2123 //                              /* use mouse + playhead */
2124 //                              start = m;
2125 //                              end = _session->audible_frame();
2126 //                      } else {
2127 //                              /* use playhead + selected marker */
2128 //                              start = _session->audible_frame();
2129 //                              end = selection->markers.front()->position();
2130 //                      }
2131 //                      break;
2132
2133 //              case EditAtMouse:
2134 //                      /* use mouse + selected marker */
2135 //                      if (selection->markers.empty()) {
2136 //                              start = m;
2137 //                              end = _session->audible_frame();
2138 //                      } else {
2139 //                              start = selection->markers.front()->position();
2140 //                              end = m;
2141 //                      }
2142 //                      break;
2143
2144 //              case EditAtSelectedMarker:
2145 //                      /* use mouse + selected marker */
2146 //                      if (selection->markers.empty()) {
2147
2148 //                              MessageDialog win (_("No edit range defined"),
2149 //                                                 false,
2150 //                                                 MESSAGE_INFO,
2151 //                                                 BUTTONS_OK);
2152
2153 //                              win.set_secondary_text (
2154 //                                      _("the edit point is Selected Marker\nbut there is no selected marker."));
2155
2156
2157 //                              win.set_default_response (RESPONSE_CLOSE);
2158 //                              win.set_position (Gtk::WIN_POS_MOUSE);
2159 //                              win.show_all();
2160
2161 //                              win.run ();
2162
2163 //                              return false; // NO RANGE
2164 //                      }
2165 //                      start = selection->markers.front()->position();
2166 //                      end = m;
2167 //                      break;
2168 //              }
2169 //      }
2170
2171 //      if (start == end) {
2172 //              return false;
2173 //      }
2174
2175 //      if (start > end) {
2176 //              swap (start, end);
2177 //      }
2178
2179         /* turn range into one delimited by start...end,
2180            not start...end-1
2181         */
2182
2183 //      end++;
2184
2185 //      return true;
2186 }
2187
2188 void
2189 Editor::deselect_all ()
2190 {
2191         begin_reversible_selection_op (X_("Deselect All"));
2192         selection->clear ();
2193         commit_reversible_selection_op ();
2194 }
2195
2196 long
2197 Editor::select_range (framepos_t s, framepos_t e)
2198 {
2199         begin_reversible_selection_op (X_("Select Range"));
2200         selection->add (clicked_axisview);
2201         selection->time.clear ();
2202         long ret = selection->set (s, e);
2203         commit_reversible_selection_op ();
2204         return ret;
2205 }