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