Fix "Select All * Edit Point" when used as shortcut. -fixes #6626
[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 && _session && !_session->deletion_in_progress()) {
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                 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1282                 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1283
1284         }
1285
1286         if (!have_non_unity_scale_amplitude || !have_audio) {
1287                 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1288         }
1289
1290         Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1291         a->set_active (have_locked && !have_unlocked);
1292         if (have_locked && have_unlocked) {
1293                 // a->set_inconsistent ();
1294         }
1295
1296         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1297         a->set_active (have_video_locked && !have_video_unlocked);
1298         if (have_video_locked && have_video_unlocked) {
1299                 // a->set_inconsistent ();
1300         }
1301
1302         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1303         a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1304
1305         if (have_position_lock_style_music && have_position_lock_style_audio) {
1306                 // a->set_inconsistent ();
1307         }
1308
1309         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1310         a->set_active (have_muted && !have_unmuted);
1311         if (have_muted && have_unmuted) {
1312                 // a->set_inconsistent ();
1313         }
1314
1315         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1316         a->set_active (have_opaque && !have_non_opaque);
1317         if (have_opaque && have_non_opaque) {
1318                 // a->set_inconsistent ();
1319         }
1320
1321         if (!have_not_at_natural_position) {
1322                 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1323         }
1324
1325         /* XXX: should also check that there is a track of the appropriate type for the selected region */
1326         if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1327                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1328         } else {
1329                 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1330         }
1331
1332         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1333         a->set_active (have_active_fade_in && !have_inactive_fade_in);
1334         if (have_active_fade_in && have_inactive_fade_in) {
1335                 // a->set_inconsistent ();
1336         }
1337
1338         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1339         a->set_active (have_active_fade_out && !have_inactive_fade_out);
1340
1341         if (have_active_fade_out && have_inactive_fade_out) {
1342                 // a->set_inconsistent ();
1343         }
1344
1345         bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1346         bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1347
1348         a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1349         a->set_active (have_active_fade && !have_inactive_fade);
1350
1351         if (have_active_fade && have_inactive_fade) {
1352                 // a->set_inconsistent ();
1353         }
1354
1355         _ignore_region_action = false;
1356
1357         _all_region_actions_sensitized = false;
1358 }
1359
1360
1361 void
1362 Editor::region_selection_changed ()
1363 {
1364         _regions->block_change_connection (true);
1365         editor_regions_selection_changed_connection.block(true);
1366
1367         if (_region_selection_change_updates_region_list) {
1368                 _regions->unselect_all ();
1369         }
1370
1371         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1372                 (*i)->set_selected_regionviews (selection->regions);
1373         }
1374
1375         if (_region_selection_change_updates_region_list) {
1376                 _regions->set_selected (selection->regions);
1377         }
1378
1379         _regions->block_change_connection (false);
1380         editor_regions_selection_changed_connection.block(false);
1381
1382         if (!_all_region_actions_sensitized) {
1383                 /* This selection change might have changed what region actions
1384                    are allowed, so sensitize them all in case a key is pressed.
1385                 */
1386                 sensitize_all_region_actions (true);
1387         }
1388
1389         if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1390                 maybe_locate_with_edit_preroll (selection->regions.start());
1391         }
1392
1393         /* propagate into backend */
1394
1395         if (_session) {
1396                 if (!selection->regions.empty()) {
1397                         _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1398                 } else {
1399                         _session->clear_object_selection ();
1400                 }
1401         }
1402
1403 }
1404
1405 void
1406 Editor::point_selection_changed ()
1407 {
1408         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1409                 (*i)->set_selected_points (selection->points);
1410         }
1411 }
1412
1413 void
1414 Editor::select_all_in_track (Selection::Operation op)
1415 {
1416         list<Selectable *> touched;
1417
1418         if (!clicked_routeview) {
1419                 return;
1420         }
1421
1422         begin_reversible_selection_op (X_("Select All in Track"));
1423
1424         clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1425
1426         switch (op) {
1427         case Selection::Toggle:
1428                 selection->add (touched);
1429                 break;
1430         case Selection::Set:
1431                 selection->set (touched);
1432                 break;
1433         case Selection::Extend:
1434                 /* meaningless, because we're selecting everything */
1435                 break;
1436         case Selection::Add:
1437                 selection->add (touched);
1438                 break;
1439         }
1440
1441         commit_reversible_selection_op ();
1442 }
1443
1444 bool
1445 Editor::select_all_internal_edit (Selection::Operation)
1446 {
1447         bool selected = false;
1448
1449         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1450                 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1451                 if (mrv) {
1452                         mrv->select_all_notes ();
1453                         selected = true;
1454                 }
1455         }
1456
1457         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1458         if (mrv) {
1459                 mrv->select_all_notes ();
1460                 selected = true;
1461         }
1462
1463         return selected;
1464 }
1465
1466 void
1467 Editor::select_all_objects (Selection::Operation op)
1468 {
1469         list<Selectable *> touched;
1470
1471         TrackViewList ts  = track_views;
1472
1473         if (internal_editing() && select_all_internal_edit(op)) {
1474                 return;  // Selected notes
1475         }
1476
1477         for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1478                 if ((*iter)->hidden()) {
1479                         continue;
1480                 }
1481                 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1482                 selection->add (*iter);
1483         }
1484
1485
1486         begin_reversible_selection_op (X_("select all"));
1487         switch (op) {
1488         case Selection::Add:
1489                 selection->add (touched);
1490                 break;
1491         case Selection::Toggle:
1492                 selection->add (touched);
1493                 break;
1494         case Selection::Set:
1495                 selection->set (touched);
1496                 break;
1497         case Selection::Extend:
1498                 /* meaningless, because we're selecting everything */
1499                 break;
1500         }
1501         commit_reversible_selection_op ();
1502 }
1503
1504 void
1505 Editor::invert_selection_in_track ()
1506 {
1507         list<Selectable *> touched;
1508
1509         if (!clicked_routeview) {
1510                 return;
1511         }
1512
1513         begin_reversible_selection_op (X_("Invert Selection in Track"));
1514         clicked_routeview->get_inverted_selectables (*selection, touched);
1515         selection->set (touched);
1516         commit_reversible_selection_op ();
1517 }
1518
1519 void
1520 Editor::invert_selection ()
1521 {
1522         list<Selectable *> touched;
1523
1524         if (internal_editing()) {
1525                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1526                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1527                         if (mrv) {
1528                                 mrv->invert_selection ();
1529                         }
1530                 }
1531                 return;
1532         }
1533
1534         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1535                 if ((*iter)->hidden()) {
1536                         continue;
1537                 }
1538                 (*iter)->get_inverted_selectables (*selection, touched);
1539         }
1540
1541         begin_reversible_selection_op (X_("Invert Selection"));
1542         selection->set (touched);
1543         commit_reversible_selection_op ();
1544 }
1545
1546 /** @param start Start time in session frames.
1547  *  @param end End time in session frames.
1548  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1549  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1550  *  @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1551  *  within the region are already selected.
1552  */
1553 void
1554 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1555 {
1556         list<Selectable*> found;
1557
1558         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1559
1560                 if ((*iter)->hidden()) {
1561                         continue;
1562                 }
1563
1564                 (*iter)->get_selectables (start, end, top, bot, found);
1565         }
1566
1567         if (found.empty()) {
1568                 selection->clear_objects();
1569                 selection->clear_time ();
1570                 return;
1571         }
1572
1573         if (preserve_if_selected && op != Selection::Toggle) {
1574                 list<Selectable*>::iterator i = found.begin();
1575                 while (i != found.end() && (*i)->get_selected()) {
1576                         ++i;
1577                 }
1578
1579                 if (i == found.end()) {
1580                         return;
1581                 }
1582         }
1583
1584         begin_reversible_selection_op (X_("select all within"));
1585         switch (op) {
1586         case Selection::Add:
1587                 selection->add (found);
1588                 break;
1589         case Selection::Toggle:
1590                 selection->toggle (found);
1591                 break;
1592         case Selection::Set:
1593                 selection->set (found);
1594                 break;
1595         case Selection::Extend:
1596                 /* not defined yet */
1597                 break;
1598         }
1599
1600         commit_reversible_selection_op ();
1601 }
1602
1603 void
1604 Editor::set_selection_from_region ()
1605 {
1606         if (selection->regions.empty()) {
1607                 return;
1608         }
1609
1610         /* find all the tracks that have selected regions */
1611
1612         set<TimeAxisView*> tracks;
1613
1614         for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1615                 tracks.insert (&(*r)->get_time_axis_view());
1616         }
1617
1618         TrackViewList tvl;
1619         tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1620
1621         /* select range (this will clear the region selection) */
1622
1623         selection->set (selection->regions.start(), selection->regions.end_frame());
1624
1625         /* and select the tracks */
1626
1627         selection->set (tvl);
1628
1629         if (!Profile->get_sae()) {
1630                 set_mouse_mode (Editing::MouseRange, false);
1631         }
1632 }
1633
1634 void
1635 Editor::set_selection_from_punch()
1636 {
1637         Location* location;
1638
1639         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1640                 return;
1641         }
1642
1643         set_selection_from_range (*location);
1644 }
1645
1646 void
1647 Editor::set_selection_from_loop()
1648 {
1649         Location* location;
1650
1651         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1652                 return;
1653         }
1654         set_selection_from_range (*location);
1655 }
1656
1657 void
1658 Editor::set_selection_from_range (Location& loc)
1659 {
1660         begin_reversible_selection_op (X_("set selection from range"));
1661         selection->set (loc.start(), loc.end());
1662         commit_reversible_selection_op ();
1663
1664         if (!Profile->get_sae()) {
1665                 set_mouse_mode (Editing::MouseRange, false);
1666         }
1667 }
1668
1669 void
1670 Editor::select_all_selectables_using_time_selection ()
1671 {
1672         list<Selectable *> touched;
1673
1674         if (selection->time.empty()) {
1675                 return;
1676         }
1677
1678         framepos_t start = selection->time[clicked_selection].start;
1679         framepos_t end = selection->time[clicked_selection].end;
1680
1681         if (end - start < 1)  {
1682                 return;
1683         }
1684
1685         TrackViewList* ts;
1686
1687         if (selection->tracks.empty()) {
1688                 ts = &track_views;
1689         } else {
1690                 ts = &selection->tracks;
1691         }
1692
1693         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1694                 if ((*iter)->hidden()) {
1695                         continue;
1696                 }
1697                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1698         }
1699
1700         begin_reversible_selection_op (X_("select all from range"));
1701         selection->set (touched);
1702         commit_reversible_selection_op ();
1703 }
1704
1705
1706 void
1707 Editor::select_all_selectables_using_punch()
1708 {
1709         Location* location = _session->locations()->auto_punch_location();
1710         list<Selectable *> touched;
1711
1712         if (location == 0 || (location->end() - location->start() <= 1))  {
1713                 return;
1714         }
1715
1716
1717         TrackViewList* ts;
1718
1719         if (selection->tracks.empty()) {
1720                 ts = &track_views;
1721         } else {
1722                 ts = &selection->tracks;
1723         }
1724
1725         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1726                 if ((*iter)->hidden()) {
1727                         continue;
1728                 }
1729                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1730         }
1731         begin_reversible_selection_op (X_("select all from punch"));
1732         selection->set (touched);
1733         commit_reversible_selection_op ();
1734
1735 }
1736
1737 void
1738 Editor::select_all_selectables_using_loop()
1739 {
1740         Location* location = _session->locations()->auto_loop_location();
1741         list<Selectable *> touched;
1742
1743         if (location == 0 || (location->end() - location->start() <= 1))  {
1744                 return;
1745         }
1746
1747
1748         TrackViewList* ts;
1749
1750         if (selection->tracks.empty()) {
1751                 ts = &track_views;
1752         } else {
1753                 ts = &selection->tracks;
1754         }
1755
1756         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1757                 if ((*iter)->hidden()) {
1758                         continue;
1759                 }
1760                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1761         }
1762         begin_reversible_selection_op (X_("select all from loop"));
1763         selection->set (touched);
1764         commit_reversible_selection_op ();
1765
1766 }
1767
1768 void
1769 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1770 {
1771         framepos_t start;
1772         framepos_t end;
1773         list<Selectable *> touched;
1774
1775         if (after) {
1776                 start = cursor->current_frame();
1777                 end = _session->current_end_frame();
1778         } else {
1779                 if (cursor->current_frame() > 0) {
1780                         start = 0;
1781                         end = cursor->current_frame() - 1;
1782                 } else {
1783                         return;
1784                 }
1785         }
1786
1787         if (internal_editing()) {
1788                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1789                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1790                         if (mrv) {
1791                                 mrv->select_range (start, end);
1792                         }
1793                 }
1794                 return;
1795         }
1796
1797         if (after) {
1798                 begin_reversible_selection_op (X_("select all after cursor"));
1799         } else {
1800                 begin_reversible_selection_op (X_("select all before cursor"));
1801         }
1802
1803         TrackViewList* ts;
1804
1805         if (selection->tracks.empty()) {
1806                 ts = &track_views;
1807         } else {
1808                 ts = &selection->tracks;
1809         }
1810
1811         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1812                 if ((*iter)->hidden()) {
1813                         continue;
1814                 }
1815                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1816         }
1817         selection->set (touched);
1818         commit_reversible_selection_op ();
1819 }
1820
1821 void
1822 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1823 {
1824         framepos_t start;
1825         framepos_t end;
1826         list<Selectable *> touched;
1827
1828         if (after) {
1829                 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1830                 end = _session->current_end_frame();
1831         } else {
1832                 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1833                         start = 0;
1834                         end -= 1;
1835                 } else {
1836                         return;
1837                 }
1838         }
1839
1840         if (internal_editing()) {
1841                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1843                         mrv->select_range (start, end);
1844                 }
1845                 return;
1846         }
1847
1848         if (after) {
1849                 begin_reversible_selection_op (X_("select all after edit"));
1850         } else {
1851                 begin_reversible_selection_op (X_("select all before edit"));
1852         }
1853
1854         TrackViewList* ts;
1855
1856         if (selection->tracks.empty()) {
1857                 ts = &track_views;
1858         } else {
1859                 ts = &selection->tracks;
1860         }
1861
1862         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1863                 if ((*iter)->hidden()) {
1864                         continue;
1865                 }
1866                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1867         }
1868         selection->set (touched);
1869         commit_reversible_selection_op ();
1870 }
1871
1872 void
1873 Editor::select_all_selectables_between (bool within)
1874 {
1875         framepos_t start;
1876         framepos_t end;
1877         list<Selectable *> touched;
1878
1879         if (!get_edit_op_range (start, end)) {
1880                 return;
1881         }
1882
1883         if (internal_editing()) {
1884                 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1885                         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1886                         mrv->select_range (start, end);
1887                 }
1888                 return;
1889         }
1890
1891         TrackViewList* ts;
1892
1893         if (selection->tracks.empty()) {
1894                 ts = &track_views;
1895         } else {
1896                 ts = &selection->tracks;
1897         }
1898
1899         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1900                 if ((*iter)->hidden()) {
1901                         continue;
1902                 }
1903                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1904         }
1905
1906         begin_reversible_selection_op (X_("Select all Selectables Between"));
1907         selection->set (touched);
1908         commit_reversible_selection_op ();
1909 }
1910
1911 void
1912 Editor::select_range_between ()
1913 {
1914         framepos_t start;
1915         framepos_t end;
1916
1917         if ( !selection->time.empty() ) {
1918                 selection->clear_time ();
1919         }
1920
1921         if (!get_edit_op_range (start, end)) {
1922                 return;
1923         }
1924
1925         begin_reversible_selection_op (X_("Select Range Between"));
1926         set_mouse_mode (MouseRange);
1927         selection->set (start, end);
1928         commit_reversible_selection_op ();
1929 }
1930
1931 bool
1932 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1933 {
1934 //      framepos_t m;
1935 //      bool ignored;
1936
1937         /* if an explicit range exists, use it */
1938
1939         if ( (mouse_mode == MouseRange || get_smart_mode() ) &&  !selection->time.empty()) {
1940                 /* we know that these are ordered */
1941                 start = selection->time.start();
1942                 end = selection->time.end_frame();
1943                 return true;
1944         } else {
1945                 start = 0;
1946                 end = 0;
1947                 return false;
1948         }
1949
1950 //      if (!mouse_frame (m, ignored)) {
1951 //              /* mouse is not in a canvas, try playhead+selected marker.
1952 //                 this is probably most true when using menus.
1953 //              */
1954 //
1955 //              if (selection->markers.empty()) {
1956 //                      return false;
1957 //              }
1958
1959 //              start = selection->markers.front()->position();
1960 //              end = _session->audible_frame();
1961
1962 //      } else {
1963
1964 //              switch (_edit_point) {
1965 //              case EditAtPlayhead:
1966 //                      if (selection->markers.empty()) {
1967 //                              /* use mouse + playhead */
1968 //                              start = m;
1969 //                              end = _session->audible_frame();
1970 //                      } else {
1971 //                              /* use playhead + selected marker */
1972 //                              start = _session->audible_frame();
1973 //                              end = selection->markers.front()->position();
1974 //                      }
1975 //                      break;
1976
1977 //              case EditAtMouse:
1978 //                      /* use mouse + selected marker */
1979 //                      if (selection->markers.empty()) {
1980 //                              start = m;
1981 //                              end = _session->audible_frame();
1982 //                      } else {
1983 //                              start = selection->markers.front()->position();
1984 //                              end = m;
1985 //                      }
1986 //                      break;
1987
1988 //              case EditAtSelectedMarker:
1989 //                      /* use mouse + selected marker */
1990 //                      if (selection->markers.empty()) {
1991
1992 //                              MessageDialog win (_("No edit range defined"),
1993 //                                                 false,
1994 //                                                 MESSAGE_INFO,
1995 //                                                 BUTTONS_OK);
1996
1997 //                              win.set_secondary_text (
1998 //                                      _("the edit point is Selected Marker\nbut there is no selected marker."));
1999
2000
2001 //                              win.set_default_response (RESPONSE_CLOSE);
2002 //                              win.set_position (Gtk::WIN_POS_MOUSE);
2003 //                              win.show_all();
2004
2005 //                              win.run ();
2006
2007 //                              return false; // NO RANGE
2008 //                      }
2009 //                      start = selection->markers.front()->position();
2010 //                      end = m;
2011 //                      break;
2012 //              }
2013 //      }
2014
2015 //      if (start == end) {
2016 //              return false;
2017 //      }
2018
2019 //      if (start > end) {
2020 //              swap (start, end);
2021 //      }
2022
2023         /* turn range into one delimited by start...end,
2024            not start...end-1
2025         */
2026
2027 //      end++;
2028
2029 //      return true;
2030 }
2031
2032 void
2033 Editor::deselect_all ()
2034 {
2035         begin_reversible_selection_op (X_("Deselect All"));
2036         selection->clear ();
2037         commit_reversible_selection_op ();
2038 }
2039
2040 long
2041 Editor::select_range (framepos_t s, framepos_t e)
2042 {
2043         begin_reversible_selection_op (X_("Select Range"));
2044         selection->add (clicked_axisview);
2045         selection->time.clear ();
2046         long ret = selection->set (s, e);
2047         commit_reversible_selection_op ();
2048         return ret;
2049 }