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