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