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