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