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