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