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