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