Update GPL boilerplate and (C)
[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
1582         if (_session) {
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
1590         if (_session->solo_selection_active()) {
1591                 play_solo_selection(false);
1592         }
1593
1594         /* set nudge button color */
1595         if (! get_regions_from_selection_and_entered().empty()) {
1596                 /* nudge regions */
1597                 nudge_forward_button.set_name ("nudge button");
1598                 nudge_backward_button.set_name ("nudge button");
1599         } else {
1600                 /* nudge marker or playhead */
1601                 nudge_forward_button.set_name ("generic button");
1602                 nudge_backward_button.set_name ("generic button");
1603         }
1604
1605         //there are a few global Editor->Select actions which select regions even if you aren't in Object mode.
1606         //if regions are selected, we must always force the mouse mode to Object...
1607         //... otherwise the user is confusingly left with selected regions that can't be manipulated.
1608         if (!selection->regions.empty()) {
1609                 set_mouse_mode( MouseObject, false );
1610         }
1611 }
1612
1613 void
1614 Editor::point_selection_changed ()
1615 {
1616         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1617                 (*i)->set_selected_points (selection->points);
1618         }
1619 }
1620
1621 void
1622 Editor::select_all_in_track (Selection::Operation op)
1623 {
1624         list<Selectable *> touched;
1625
1626         if (!clicked_routeview) {
1627                 return;
1628         }
1629
1630         begin_reversible_selection_op (X_("Select All in Track"));
1631
1632         clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1633
1634         switch (op) {
1635         case Selection::Toggle:
1636                 selection->add (touched);
1637                 break;
1638         case Selection::Set:
1639                 selection->set (touched);
1640                 break;
1641         case Selection::Extend:
1642                 /* meaningless, because we're selecting everything */
1643                 break;
1644         case Selection::Add:
1645                 selection->add (touched);
1646                 break;
1647         }
1648
1649         commit_reversible_selection_op ();
1650 }
1651
1652 bool
1653 Editor::select_all_internal_edit (Selection::Operation)
1654 {
1655         bool selected = false;
1656
1657         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1658                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1659                 if (mrv) {
1660                         mrv->select_all_notes ();
1661                         selected = true;
1662                 }
1663         }
1664
1665         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1666         if (mrv) {
1667                 mrv->select_all_notes ();
1668                 selected = true;
1669         }
1670
1671         return selected;
1672 }
1673
1674 void
1675 Editor::select_all_objects (Selection::Operation op)
1676 {
1677         list<Selectable *> touched;
1678
1679         if (internal_editing() && select_all_internal_edit(op)) {
1680                 return;  // Selected notes
1681         }
1682
1683         TrackViewList ts;
1684
1685         if (selection->tracks.empty()) {
1686                 ts = track_views;
1687         } else {
1688                 ts = selection->tracks;
1689         }
1690
1691         for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1692                 if ((*iter)->hidden()) {
1693                         continue;
1694                 }
1695                 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1696         }
1697
1698         begin_reversible_selection_op (X_("select all"));
1699         switch (op) {
1700         case Selection::Add:
1701                 selection->add (touched);
1702                 break;
1703         case Selection::Toggle:
1704                 selection->toggle (touched);
1705                 break;
1706         case Selection::Set:
1707                 selection->set (touched);
1708                 break;
1709         case Selection::Extend:
1710                 /* meaningless, because we're selecting everything */
1711                 break;
1712         }
1713         commit_reversible_selection_op ();
1714 }
1715
1716 void
1717 Editor::invert_selection_in_track ()
1718 {
1719         list<Selectable *> touched;
1720
1721         if (!clicked_routeview) {
1722                 return;
1723         }
1724
1725         begin_reversible_selection_op (X_("Invert Selection in Track"));
1726         clicked_routeview->get_inverted_selectables (*selection, touched);
1727         selection->set (touched);
1728         commit_reversible_selection_op ();
1729 }
1730
1731 void
1732 Editor::invert_selection ()
1733 {
1734
1735         if (internal_editing()) {
1736                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1737                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1738                         if (mrv) {
1739                                 mrv->invert_selection ();
1740                         }
1741                 }
1742                 return;
1743         }
1744
1745         if (!selection->tracks.empty()) {
1746
1747                 TrackViewList inverted;
1748
1749                 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1750                         if (!(*iter)->selected()) {
1751                                 inverted.push_back (*iter);
1752                         }
1753                 }
1754
1755                 begin_reversible_selection_op (X_("Invert Track Selection"));
1756                 selection->set (inverted);
1757                 commit_reversible_selection_op ();
1758
1759         } else {
1760
1761                 list<Selectable *> touched;
1762
1763                 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1764                         if ((*iter)->hidden()) {
1765                                 continue;
1766                         }
1767                         (*iter)->get_inverted_selectables (*selection, touched);
1768                 }
1769
1770                 begin_reversible_selection_op (X_("Invert ObjectSelection"));
1771                 selection->set (touched);
1772                 commit_reversible_selection_op ();
1773         }
1774 }
1775
1776 /** @param start Start time in session samples.
1777  *  @param end End time in session samples.
1778  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1779  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1780  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1781  *  within the region are already selected.
1782  */
1783 void
1784 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1785 {
1786         list<Selectable*> found;
1787
1788         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1789
1790                 if ((*iter)->hidden()) {
1791                         continue;
1792                 }
1793
1794                 (*iter)->get_selectables (start, end, top, bot, found);
1795         }
1796
1797         if (found.empty()) {
1798                 selection->clear_objects();
1799                 selection->clear_time ();
1800                 return;
1801         }
1802
1803         if (preserve_if_selected && op != Selection::Toggle) {
1804                 list<Selectable*>::iterator i = found.begin();
1805                 while (i != found.end() && (*i)->selected()) {
1806                         ++i;
1807                 }
1808
1809                 if (i == found.end()) {
1810                         return;
1811                 }
1812         }
1813
1814         begin_reversible_selection_op (X_("select all within"));
1815         switch (op) {
1816         case Selection::Add:
1817                 selection->add (found);
1818                 break;
1819         case Selection::Toggle:
1820                 selection->toggle (found);
1821                 break;
1822         case Selection::Set:
1823                 selection->set (found);
1824                 break;
1825         case Selection::Extend:
1826                 /* not defined yet */
1827                 break;
1828         }
1829
1830         commit_reversible_selection_op ();
1831 }
1832
1833 void
1834 Editor::set_selection_from_region ()
1835 {
1836         if (selection->regions.empty()) {
1837                 return;
1838         }
1839
1840         /* find all the tracks that have selected regions */
1841
1842         set<TimeAxisView*> tracks;
1843
1844         for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1845                 tracks.insert (&(*r)->get_time_axis_view());
1846         }
1847
1848         TrackViewList tvl;
1849         tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1850
1851         /* select range (this will clear the region selection) */
1852
1853         selection->set (selection->regions.start(), selection->regions.end_sample());
1854
1855         /* and select the tracks */
1856
1857         selection->set (tvl);
1858
1859         if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1860                 set_mouse_mode (Editing::MouseRange, false);
1861         }
1862 }
1863
1864 void
1865 Editor::set_selection_from_punch()
1866 {
1867         Location* location;
1868
1869         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1870                 return;
1871         }
1872
1873         set_selection_from_range (*location);
1874 }
1875
1876 void
1877 Editor::set_selection_from_loop()
1878 {
1879         Location* location;
1880
1881         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1882                 return;
1883         }
1884         set_selection_from_range (*location);
1885 }
1886
1887 void
1888 Editor::set_selection_from_range (Location& loc)
1889 {
1890         begin_reversible_selection_op (X_("set selection from range"));
1891
1892         selection->set (loc.start(), loc.end());
1893
1894         // if no tracks are selected, enable all tracks
1895         // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1896         if (selection->tracks.empty()) {
1897                 select_all_tracks();
1898         }
1899
1900         commit_reversible_selection_op ();
1901
1902         if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1903                 set_mouse_mode (MouseRange, false);
1904         }
1905 }
1906
1907 void
1908 Editor::select_all_selectables_using_time_selection ()
1909 {
1910         list<Selectable *> touched;
1911
1912         if (selection->time.empty()) {
1913                 return;
1914         }
1915
1916         samplepos_t start = selection->time[clicked_selection].start;
1917         samplepos_t end = selection->time[clicked_selection].end;
1918
1919         if (end - start < 1)  {
1920                 return;
1921         }
1922
1923         TrackViewList* ts;
1924
1925         if (selection->tracks.empty()) {
1926                 ts = &track_views;
1927         } else {
1928                 ts = &selection->tracks;
1929         }
1930
1931         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1932                 if ((*iter)->hidden()) {
1933                         continue;
1934                 }
1935                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1936         }
1937
1938         begin_reversible_selection_op (X_("select all from range"));
1939         selection->set (touched);
1940         commit_reversible_selection_op ();
1941 }
1942
1943
1944 void
1945 Editor::select_all_selectables_using_punch()
1946 {
1947         Location* location = _session->locations()->auto_punch_location();
1948         list<Selectable *> touched;
1949
1950         if (location == 0 || (location->end() - location->start() <= 1))  {
1951                 return;
1952         }
1953
1954
1955         TrackViewList* ts;
1956
1957         if (selection->tracks.empty()) {
1958                 ts = &track_views;
1959         } else {
1960                 ts = &selection->tracks;
1961         }
1962
1963         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1964                 if ((*iter)->hidden()) {
1965                         continue;
1966                 }
1967                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1968         }
1969         begin_reversible_selection_op (X_("select all from punch"));
1970         selection->set (touched);
1971         commit_reversible_selection_op ();
1972
1973 }
1974
1975 void
1976 Editor::select_all_selectables_using_loop()
1977 {
1978         Location* location = _session->locations()->auto_loop_location();
1979         list<Selectable *> touched;
1980
1981         if (location == 0 || (location->end() - location->start() <= 1))  {
1982                 return;
1983         }
1984
1985
1986         TrackViewList* ts;
1987
1988         if (selection->tracks.empty()) {
1989                 ts = &track_views;
1990         } else {
1991                 ts = &selection->tracks;
1992         }
1993
1994         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1995                 if ((*iter)->hidden()) {
1996                         continue;
1997                 }
1998                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1999         }
2000         begin_reversible_selection_op (X_("select all from loop"));
2001         selection->set (touched);
2002         commit_reversible_selection_op ();
2003
2004 }
2005
2006 void
2007 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
2008 {
2009         samplepos_t start;
2010         samplepos_t end;
2011         list<Selectable *> touched;
2012
2013         if (after) {
2014                 start = cursor->current_sample();
2015                 end = _session->current_end_sample();
2016         } else {
2017                 if (cursor->current_sample() > 0) {
2018                         start = 0;
2019                         end = cursor->current_sample() - 1;
2020                 } else {
2021                         return;
2022                 }
2023         }
2024
2025         if (internal_editing()) {
2026                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2027                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2028                         if (mrv) {
2029                                 mrv->select_range (start, end);
2030                         }
2031                 }
2032                 return;
2033         }
2034
2035         if (after) {
2036                 begin_reversible_selection_op (X_("select all after cursor"));
2037         } else {
2038                 begin_reversible_selection_op (X_("select all before cursor"));
2039         }
2040
2041         TrackViewList* ts;
2042
2043         if (selection->tracks.empty()) {
2044                 ts = &track_views;
2045         } else {
2046                 ts = &selection->tracks;
2047         }
2048
2049         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2050                 if ((*iter)->hidden()) {
2051                         continue;
2052                 }
2053                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2054         }
2055         selection->set (touched);
2056         commit_reversible_selection_op ();
2057 }
2058
2059 void
2060 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2061 {
2062         samplepos_t start;
2063         samplepos_t end;
2064         list<Selectable *> touched;
2065
2066         if (after) {
2067                 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2068                 end = _session->current_end_sample();
2069         } else {
2070                 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2071                         start = 0;
2072                         end -= 1;
2073                 } else {
2074                         return;
2075                 }
2076         }
2077
2078         if (internal_editing()) {
2079                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2080                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2081                         mrv->select_range (start, end);
2082                 }
2083                 return;
2084         }
2085
2086         if (after) {
2087                 begin_reversible_selection_op (X_("select all after edit"));
2088         } else {
2089                 begin_reversible_selection_op (X_("select all before edit"));
2090         }
2091
2092         TrackViewList* ts;
2093
2094         if (selection->tracks.empty()) {
2095                 ts = &track_views;
2096         } else {
2097                 ts = &selection->tracks;
2098         }
2099
2100         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2101                 if ((*iter)->hidden()) {
2102                         continue;
2103                 }
2104                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2105         }
2106         selection->set (touched);
2107         commit_reversible_selection_op ();
2108 }
2109
2110 void
2111 Editor::select_all_selectables_between (bool within)
2112 {
2113         samplepos_t start;
2114         samplepos_t end;
2115         list<Selectable *> touched;
2116
2117         if (!get_edit_op_range (start, end)) {
2118                 return;
2119         }
2120
2121         if (internal_editing()) {
2122                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2123                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2124                         mrv->select_range (start, end);
2125                 }
2126                 return;
2127         }
2128
2129         TrackViewList* ts;
2130
2131         if (selection->tracks.empty()) {
2132                 ts = &track_views;
2133         } else {
2134                 ts = &selection->tracks;
2135         }
2136
2137         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2138                 if ((*iter)->hidden()) {
2139                         continue;
2140                 }
2141                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2142         }
2143
2144         begin_reversible_selection_op (X_("Select all Selectables Between"));
2145         selection->set (touched);
2146         commit_reversible_selection_op ();
2147 }
2148
2149 void
2150 Editor::select_range_between ()
2151 {
2152         samplepos_t start;
2153         samplepos_t end;
2154
2155         if (!selection->time.empty()) {
2156                 selection->clear_time ();
2157         }
2158
2159         if (!get_edit_op_range (start, end)) {
2160                 return;
2161         }
2162
2163         if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2164                 set_mouse_mode (MouseRange, false);
2165         }
2166
2167         begin_reversible_selection_op (X_("Select Range Between"));
2168         selection->set (start, end);
2169         commit_reversible_selection_op ();
2170 }
2171
2172 bool
2173 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2174 {
2175         /* if an explicit range exists, use it */
2176
2177         if ((mouse_mode == MouseRange || get_smart_mode()) &&  !selection->time.empty()) {
2178                 /* we know that these are ordered */
2179                 start = selection->time.start();
2180                 end = selection->time.end_sample();
2181                 return true;
2182         } else {
2183                 start = 0;
2184                 end = 0;
2185                 return false;
2186         }
2187 }
2188
2189 void
2190 Editor::deselect_all ()
2191 {
2192         begin_reversible_selection_op (X_("Deselect All"));
2193         selection->clear ();
2194         commit_reversible_selection_op ();
2195 }
2196
2197 long
2198 Editor::select_range (samplepos_t s, samplepos_t e)
2199 {
2200         begin_reversible_selection_op (X_("Select Range"));
2201         selection->add (clicked_axisview);
2202         selection->time.clear ();
2203         long ret = selection->set (s, e);
2204         commit_reversible_selection_op ();
2205         return ret;
2206 }