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