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