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