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