Fix up selection of automation points.
[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/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29
30 #include "editor.h"
31 #include "actions.h"
32 #include "audio_time_axis.h"
33 #include "audio_region_view.h"
34 #include "audio_streamview.h"
35 #include "automation_line.h"
36 #include "control_point.h"
37 #include "editor_regions.h"
38
39 #include "i18n.h"
40
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44 using namespace Gtk;
45 using namespace Glib;
46 using namespace Gtkmm2ext;
47 using namespace Editing;
48
49 struct TrackViewByPositionSorter
50 {
51     bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
52             return a->y_position() < b->y_position();
53     }
54 };
55
56 bool
57 Editor::extend_selection_to_track (TimeAxisView& view)
58 {
59         if (selection->selected (&view)) {
60                 /* already selected, do nothing */
61                 return false;
62         }
63
64         if (selection->tracks.empty()) {
65
66                 if (!selection->selected (&view)) {
67                         selection->set (&view);
68                         return true;
69                 } else {
70                         return false;
71                 }
72         }
73
74         /* something is already selected, so figure out which range of things to add */
75
76         TrackViewList to_be_added;
77         TrackViewList sorted = track_views;
78         TrackViewByPositionSorter cmp;
79         bool passed_clicked = false;
80         bool forwards = true;
81
82         sorted.sort (cmp);
83
84         if (!selection->selected (&view)) {
85                 to_be_added.push_back (&view);
86         }
87
88         /* figure out if we should go forward or backwards */
89
90         for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
91
92                 if ((*i) == &view) {
93                         passed_clicked = true;
94                 }
95
96                 if (selection->selected (*i)) {
97                         if (passed_clicked) {
98                                 forwards = true;
99                         } else {
100                                 forwards = false;
101                         }
102                         break;
103                 }
104         }
105
106         passed_clicked = false;
107
108         if (forwards) {
109
110                 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
111
112                         if ((*i) == &view) {
113                                 passed_clicked = true;
114                                 continue;
115                         }
116
117                         if (passed_clicked) {
118                                 if ((*i)->hidden()) {
119                                         continue;
120                                 }
121                                 if (selection->selected (*i)) {
122                                         break;
123                                 } else if (!(*i)->hidden()) {
124                                         to_be_added.push_back (*i);
125                                 }
126                         }
127                 }
128
129         } else {
130
131                 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
132
133                         if ((*r) == &view) {
134                                 passed_clicked = true;
135                                 continue;
136                         }
137
138                         if (passed_clicked) {
139
140                                 if ((*r)->hidden()) {
141                                         continue;
142                                 }
143
144                                 if (selection->selected (*r)) {
145                                         break;
146                                 } else if (!(*r)->hidden()) {
147                                         to_be_added.push_back (*r);
148                                 }
149                         }
150                 }
151         }
152
153         if (!to_be_added.empty()) {
154                 selection->add (to_be_added);
155                 return true;
156         }
157
158         return false;
159 }
160
161 void
162 Editor::select_all_tracks ()
163 {
164         TrackViewList visible_views;
165         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
166                 if ((*i)->marked_for_display()) {
167                         visible_views.push_back (*i);
168                 }
169         }
170         selection->set (visible_views);
171 }
172
173 /** Select clicked_axisview, unless there are no currently selected
174  *  tracks, in which case nothing will happen unless `force' is true.
175  */
176 void
177 Editor::set_selected_track_as_side_effect (bool force)
178 {
179         if (!clicked_axisview) {
180                 return;
181         }
182
183         if (!selection->tracks.empty()) {
184                 if (!selection->selected (clicked_axisview)) {
185                         selection->add (clicked_axisview);
186                 }
187
188         } else if (force) {
189                 selection->set (clicked_axisview);
190         }
191 }
192
193 void
194 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
195 {
196         switch (op) {
197         case Selection::Toggle:
198                 if (selection->selected (&view)) {
199                         if (!no_remove) {
200                                 selection->remove (&view);
201                         }
202                 } else {
203                         selection->add (&view);
204                 }
205                 break;
206
207         case Selection::Add:
208                 if (!selection->selected (&view)) {
209                         selection->add (&view);
210                 }
211                 break;
212
213         case Selection::Set:
214                 selection->set (&view);
215                 break;
216
217         case Selection::Extend:
218                 extend_selection_to_track (view);
219                 break;
220         }
221 }
222
223 void
224 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
225 {
226         if (!clicked_routeview) {
227                 return;
228         }
229
230         if (!press) {
231                 return;
232         }
233
234         set_selected_track (*clicked_routeview, op, no_remove);
235 }
236
237 bool
238 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
239 {
240         if (!clicked_control_point) {
241                 return false;
242         }
243
244         if (clicked_control_point->selected()) {
245                 /* the clicked control point is already selected; others may be as well, so
246                    don't change the selection.
247                 */
248                 return true;
249         }
250
251         /* We know the ControlPoint that was clicked, but (as discussed in automation_selectable.h)
252          * selected automation data are described by areas on the AutomationLine.  A ControlPoint
253          * represents any model points in the space that it takes up, so the AutomationSelectable
254          * needs to be the size of the ControlPoint.
255          */
256
257         double const size = clicked_control_point->size ();
258         AutomationLine& line = clicked_control_point->line ();
259         
260         nframes64_t const x1 = pixel_to_frame (clicked_control_point->get_x() - size / 2) + line.time_converter().origin_b ();
261         nframes64_t const x2 = pixel_to_frame (clicked_control_point->get_x() + size / 2) + line.time_converter().origin_b ();
262         double y1 = clicked_control_point->get_y() - size / 2;
263         double y2 = clicked_control_point->get_y() + size / 2;
264
265         /* convert the y values to trackview space */
266         double dummy = 0;
267         clicked_control_point->line().parent_group().i2w (dummy, y1);
268         clicked_control_point->line().parent_group().i2w (dummy, y2);
269         _trackview_group->w2i (dummy, y1);
270         _trackview_group->w2i (dummy, y2);
271
272         /* and set up the selection */
273         return select_all_within (x1, x2, y1, y2, selection->tracks, op);
274 }
275
276 void
277 Editor::get_onscreen_tracks (TrackViewList& tvl)
278 {
279         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
280                 if ((*i)->y_position() < _canvas_height) {
281                         tvl.push_back (*i);
282                 }
283         }
284 }
285
286 /** Call a slot for a given `basis' track and also for any track that is in the same
287  *  active route group with a particular set of properties.
288  *
289  *  @param sl Slot to call.
290  *  @param basis Basis track.
291  *  @param prop Properties that active edit groups must share to be included in the map.
292  */
293
294 void
295 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
296 {
297         RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
298
299         if (route_basis == 0) {
300                 return;
301         }
302
303         set<RouteTimeAxisView*> tracks;
304         tracks.insert (route_basis);
305
306         RouteGroup* group = route_basis->route()->route_group();
307
308         if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
309
310                 /* the basis is a member of an active route group, with the appropriate
311                    properties; find other members */
312
313                 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
314                         RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
315                         if (v && v->route()->route_group() == group) {
316                                 tracks.insert (v);
317                         }
318                 }
319         }
320
321         /* call the slots */
322         uint32_t const sz = tracks.size ();
323         
324         for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
325                 sl (**i, sz);
326         }
327 }
328
329 void
330 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
331 {
332         boost::shared_ptr<Playlist> pl;
333         vector<boost::shared_ptr<Region> > results;
334         RegionView* marv;
335         boost::shared_ptr<Track> tr;
336
337         if ((tr = tv.track()) == 0) {
338                 /* bus */
339                 return;
340         }
341
342         if (&tv == &basis->get_time_axis_view()) {
343                 /* looking in same track as the original */
344                 return;
345         }
346
347         if ((pl = tr->playlist()) != 0) {
348                 pl->get_equivalent_regions (basis->region(), results);
349         }
350
351         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
352                 if ((marv = tv.view()->find_view (*ir)) != 0) {
353                         all_equivs->push_back (marv);
354                 }
355         }
356 }
357
358 void
359 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
360 {
361         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview(), property);
362
363         /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
364
365         equivalent_regions.push_back (basis);
366 }
367
368 RegionSelection
369 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
370 {
371         RegionSelection equivalent;
372
373         for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
374
375                 vector<RegionView*> eq;
376
377                 mapover_tracks (
378                         sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
379                         &(*i)->get_trackview(), prop
380                         );
381
382                 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
383                         equivalent.add (*j);
384                 }
385
386                 equivalent.add (*i);
387         }
388
389         return equivalent;
390 }
391
392
393 int
394 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
395 {
396         int region_count = 0;
397
398         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
399
400                 RouteTimeAxisView* tatv;
401
402                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
403
404                         boost::shared_ptr<Playlist> pl;
405                         vector<boost::shared_ptr<Region> > results;
406                         RegionView* marv;
407                         boost::shared_ptr<Track> tr;
408
409                         if ((tr = tatv->track()) == 0) {
410                                 /* bus */
411                                 continue;
412                         }
413
414                         if ((pl = (tr->playlist())) != 0) {
415                                 pl->get_region_list_equivalent_regions (region, results);
416                         }
417
418                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
419                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
420                                         region_count++;
421                                 }
422                         }
423
424                 }
425         }
426
427         return region_count;
428 }
429
430
431 bool
432 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
433 {
434         vector<RegionView*> all_equivalent_regions;
435         bool commit = false;
436
437         if (!clicked_regionview || !clicked_routeview) {
438                 return false;
439         }
440
441         if (press) {
442                 button_release_can_deselect = false;
443         }
444
445         if (op == Selection::Toggle || op == Selection::Set) {
446
447
448                 switch (op) {
449                 case Selection::Toggle:
450
451                         if (selection->selected (clicked_regionview)) {
452                                 if (press) {
453
454                                         /* whatever was clicked was selected already; do nothing here but allow
455                                            the button release to deselect it
456                                         */
457
458                                         button_release_can_deselect = true;
459
460                                 } else {
461
462                                         if (button_release_can_deselect) {
463
464                                                 /* just remove this one region, but only on a permitted button release */
465
466                                                 selection->remove (clicked_regionview);
467                                                 commit = true;
468
469                                                 /* no more deselect action on button release till a new press
470                                                    finds an already selected object.
471                                                 */
472
473                                                 button_release_can_deselect = false;
474                                         }
475                                 }
476
477                         } else {
478
479                                 if (press) {
480
481                                         if (selection->selected (clicked_routeview)) {
482                                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
483                                         } else {
484                                                 all_equivalent_regions.push_back (clicked_regionview);
485                                         }
486
487                                         /* add all the equivalent regions, but only on button press */
488
489                                         if (!all_equivalent_regions.empty()) {
490                                                 commit = true;
491                                         }
492
493                                         selection->add (all_equivalent_regions);
494                                 }
495                         }
496                         break;
497
498                 case Selection::Set:
499                         if (!selection->selected (clicked_regionview)) {
500                                 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
501                                 selection->set (all_equivalent_regions);
502                                 commit = true;
503                         } else {
504                                 /* no commit necessary: clicked on an already selected region */
505                                 goto out;
506                         }
507                         break;
508
509                 default:
510                         /* silly compiler */
511                         break;
512                 }
513
514         } else if (op == Selection::Extend) {
515
516                 list<Selectable*> results;
517                 nframes64_t last_frame;
518                 nframes64_t first_frame;
519                 bool same_track = false;
520
521                 /* 1. find the last selected regionview in the track that was clicked in */
522
523                 last_frame = 0;
524                 first_frame = max_frames;
525
526                 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
527                         if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
528
529                                 if ((*x)->region()->last_frame() > last_frame) {
530                                         last_frame = (*x)->region()->last_frame();
531                                 }
532
533                                 if ((*x)->region()->first_frame() < first_frame) {
534                                         first_frame = (*x)->region()->first_frame();
535                                 }
536
537                                 same_track = true;
538                         }
539                 }
540
541                 if (same_track) {
542
543                         /* 2. figure out the boundaries for our search for new objects */
544
545                         switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
546                         case OverlapNone:
547                                 if (last_frame < clicked_regionview->region()->first_frame()) {
548                                         first_frame = last_frame;
549                                         last_frame = clicked_regionview->region()->last_frame();
550                                 } else {
551                                         last_frame = first_frame;
552                                         first_frame = clicked_regionview->region()->first_frame();
553                                 }
554                                 break;
555
556                         case OverlapExternal:
557                                 if (last_frame < clicked_regionview->region()->first_frame()) {
558                                         first_frame = last_frame;
559                                         last_frame = clicked_regionview->region()->last_frame();
560                                 } else {
561                                         last_frame = first_frame;
562                                         first_frame = clicked_regionview->region()->first_frame();
563                                 }
564                                 break;
565
566                         case OverlapInternal:
567                                 if (last_frame < clicked_regionview->region()->first_frame()) {
568                                         first_frame = last_frame;
569                                         last_frame = clicked_regionview->region()->last_frame();
570                                 } else {
571                                         last_frame = first_frame;
572                                         first_frame = clicked_regionview->region()->first_frame();
573                                 }
574                                 break;
575
576                         case OverlapStart:
577                         case OverlapEnd:
578                                 /* nothing to do except add clicked region to selection, since it
579                                    overlaps with the existing selection in this track.
580                                 */
581                                 break;
582                         }
583
584                 } else {
585
586                         /* click in a track that has no regions selected, so extend vertically
587                            to pick out all regions that are defined by the existing selection
588                            plus this one.
589                         */
590
591
592                         first_frame = entered_regionview->region()->position();
593                         last_frame = entered_regionview->region()->last_frame();
594
595                         for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
596                                 if ((*i)->region()->position() < first_frame) {
597                                         first_frame = (*i)->region()->position();
598                                 }
599                                 if ((*i)->region()->last_frame() + 1 > last_frame) {
600                                         last_frame = (*i)->region()->last_frame();
601                                 }
602                         }
603                 }
604
605                 /* 2. find all the tracks we should select in */
606
607                 set<RouteTimeAxisView*> relevant_tracks;
608
609                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
610                         RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
611                         if (r) {
612                                 relevant_tracks.insert (r);
613                         }
614                 }
615                 
616                 set<RouteTimeAxisView*> already_in_selection;
617
618                 if (relevant_tracks.empty()) {
619
620                         /* no tracks selected .. thus .. if the
621                            regionview we're in isn't selected
622                            (i.e. we're about to extend to it), then
623                            find all tracks between the this one and
624                            any selected ones.
625                         */
626
627                         if (!selection->selected (entered_regionview)) {
628
629                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
630
631                                 if (rtv) {
632
633                                         /* add this track to the ones we will search */
634
635                                         relevant_tracks.insert (rtv);
636
637                                         /* find the track closest to this one that
638                                            already a selected region.
639                                         */
640
641                                         RouteTimeAxisView* closest = 0;
642                                         int distance = INT_MAX;
643                                         int key = rtv->route()->order_key ("editor");
644
645                                         for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
646
647                                                 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
648
649                                                 if (artv && artv != rtv) {
650
651                                                         pair<set<RouteTimeAxisView*>::iterator,bool> result;
652
653                                                         result = already_in_selection.insert (artv);
654
655                                                         if (result.second) {
656                                                                 /* newly added to already_in_selection */
657
658                                                                 int d = artv->route()->order_key ("editor");
659
660                                                                 d -= key;
661
662                                                                 if (abs (d) < distance) {
663                                                                         distance = abs (d);
664                                                                         closest = artv;
665                                                                 }
666                                                         }
667                                                 }
668                                         }
669
670                                         if (closest) {
671
672                                                 /* now add all tracks between that one and this one */
673
674                                                 int okey = closest->route()->order_key ("editor");
675
676                                                 if (okey > key) {
677                                                         swap (okey, key);
678                                                 }
679
680                                                 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
681                                                         RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
682                                                         if (artv && artv != rtv) {
683
684                                                                 int k = artv->route()->order_key ("editor");
685
686                                                                 if (k >= okey && k <= key) {
687
688                                                                         /* in range but don't add it if
689                                                                            it already has tracks selected.
690                                                                            this avoids odd selection
691                                                                            behaviour that feels wrong.
692                                                                         */
693
694                                                                         if (find (already_in_selection.begin(),
695                                                                                   already_in_selection.end(),
696                                                                                   artv) == already_in_selection.end()) {
697
698                                                                                 relevant_tracks.insert (artv);
699                                                                         }
700                                                                 }
701                                                         }
702                                                 }
703                                         }
704                                 }
705                         }
706                 }
707
708                 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
709                            one that was clicked.
710                 */
711
712                 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
713                         (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
714                 }
715
716                 /* 4. convert to a vector of regions */
717
718                 vector<RegionView*> regions;
719
720                 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
721                         RegionView* arv;
722
723                         if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
724                                 regions.push_back (arv);
725                         }
726                 }
727
728                 if (!regions.empty()) {
729                         selection->add (regions);
730                         commit = true;
731                 }
732         }
733
734   out:
735         return commit;
736 }
737
738
739 void
740 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
741 {
742         vector<RegionView*> all_equivalent_regions;
743
744         get_regions_corresponding_to (region, all_equivalent_regions);
745
746         if (all_equivalent_regions.empty()) {
747                 return;
748         }
749
750         begin_reversible_command (_("set selected regions"));
751
752         switch (op) {
753         case Selection::Toggle:
754                 /* XXX this is not correct */
755                 selection->toggle (all_equivalent_regions);
756                 break;
757         case Selection::Set:
758                 selection->set (all_equivalent_regions);
759                 break;
760         case Selection::Extend:
761                 selection->add (all_equivalent_regions);
762                 break;
763         case Selection::Add:
764                 selection->add (all_equivalent_regions);
765                 break;
766         }
767
768         commit_reversible_command () ;
769 }
770
771 bool
772 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
773 {
774         RegionView* rv;
775         boost::shared_ptr<Region> r (weak_r.lock());
776
777         if (!r) {
778                 return true;
779         }
780
781         if ((rv = sv->find_view (r)) == 0) {
782                 return true;
783         }
784
785         /* don't reset the selection if its something other than
786            a single other region.
787         */
788
789         if (selection->regions.size() > 1) {
790                 return true;
791         }
792
793         begin_reversible_command (_("set selected regions"));
794
795         selection->set (rv);
796
797         commit_reversible_command () ;
798
799         return true;
800 }
801
802 void
803 Editor::track_selection_changed ()
804 {
805         switch (selection->tracks.size()){
806         case 0:
807                 break;
808         default:
809                 set_selected_mixer_strip (*(selection->tracks.front()));
810                 break;
811         }
812
813         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
814                 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
815                         (*i)->set_selected (true);
816                 } else {
817                         (*i)->set_selected (false);
818                 }
819         }
820
821         ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
822 }
823
824 void
825 Editor::time_selection_changed ()
826 {
827         if (Profile->get_sae()) {
828                 return;
829         }
830
831         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
832                 (*i)->hide_selection ();
833         }
834
835         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
836                 (*i)->show_selection (selection->time);
837         }
838
839         if (selection->time.empty()) {
840                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
841         } else {
842                 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
843         }
844 }
845
846 void
847 Editor::sensitize_the_right_region_actions (bool have_selected_regions)
848 {
849         for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
850              x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
851
852                 string accel_path = (*x)->get_accel_path ();
853                 AccelKey key;
854
855                 /* if there is an accelerator, it should always be sensitive
856                    to allow for keyboard ops on entered regions.
857                 */
858
859                 bool known = ActionManager::lookup_entry (accel_path, key);
860
861                 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
862                         (*x)->set_sensitive (true);
863                 } else {
864                         (*x)->set_sensitive (have_selected_regions);
865                 }
866         }
867 }
868
869
870 void
871 Editor::region_selection_changed ()
872 {
873         _regions->block_change_connection (true);
874         editor_regions_selection_changed_connection.block(true);
875
876         _regions->unselect_all ();
877
878         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
879                 (*i)->set_selected_regionviews (selection->regions);
880         }
881
882         _regions->set_selected (selection->regions);
883
884         sensitize_the_right_region_actions (!selection->regions.empty());
885
886         _regions->block_change_connection (false);
887         editor_regions_selection_changed_connection.block(false);
888 }
889
890 void
891 Editor::point_selection_changed ()
892 {
893         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
894                 (*i)->set_selected_points (selection->points);
895         }
896 }
897
898 void
899 Editor::select_all_in_track (Selection::Operation op)
900 {
901         list<Selectable *> touched;
902
903         if (!clicked_routeview) {
904                 return;
905         }
906
907         clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
908
909         switch (op) {
910         case Selection::Toggle:
911                 selection->add (touched);
912                 break;
913         case Selection::Set:
914                 selection->set (touched);
915                 break;
916         case Selection::Extend:
917                 /* meaningless, because we're selecting everything */
918                 break;
919         case Selection::Add:
920                 selection->add (touched);
921                 break;
922         }
923 }
924
925 void
926 Editor::select_all (Selection::Operation op)
927 {
928         list<Selectable *> touched;
929
930         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
931                 if ((*iter)->hidden()) {
932                         continue;
933                 }
934                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
935         }
936         begin_reversible_command (_("select all"));
937         switch (op) {
938         case Selection::Add:
939                 selection->add (touched);
940                 break;
941         case Selection::Toggle:
942                 selection->add (touched);
943                 break;
944         case Selection::Set:
945                 selection->set (touched);
946                 break;
947         case Selection::Extend:
948                 /* meaningless, because we're selecting everything */
949                 break;
950         }
951         commit_reversible_command ();
952 }
953 void
954 Editor::invert_selection_in_track ()
955 {
956         list<Selectable *> touched;
957
958         if (!clicked_routeview) {
959                 return;
960         }
961
962         clicked_routeview->get_inverted_selectables (*selection, touched);
963         selection->set (touched);
964 }
965
966 void
967 Editor::invert_selection ()
968 {
969         list<Selectable *> touched;
970
971         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
972                 if ((*iter)->hidden()) {
973                         continue;
974                 }
975                 (*iter)->get_inverted_selectables (*selection, touched);
976         }
977
978         selection->set (touched);
979 }
980
981 /** @param start Start time in session frames.
982  *  @param end End time in session frames.
983  *  @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
984  *  @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
985  */
986 bool
987 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
988 {
989         list<Selectable*> found;
990
991         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
992                 
993                 if ((*iter)->hidden()) {
994                         continue;
995                 }
996
997                 (*iter)->get_selectables (start, end, top, bot, found);
998         }
999
1000         if (found.empty()) {
1001                 return false;
1002         }
1003
1004         begin_reversible_command (_("select all within"));
1005         switch (op) {
1006         case Selection::Add:
1007                 selection->add (found);
1008                 break;
1009         case Selection::Toggle:
1010                 selection->toggle (found);
1011                 break;
1012         case Selection::Set:
1013                 selection->set (found);
1014                 break;
1015         case Selection::Extend:
1016                 /* not defined yet */
1017                 break;
1018         }
1019
1020         commit_reversible_command ();
1021
1022         return !found.empty();
1023 }
1024
1025 void
1026 Editor::set_selection_from_region ()
1027 {
1028         if (selection->regions.empty()) {
1029                 return;
1030         }
1031
1032         selection->set (selection->regions.start(), selection->regions.end_frame());
1033         if (!Profile->get_sae()) {
1034                 set_mouse_mode (Editing::MouseRange, false);
1035         }
1036 }
1037
1038 void
1039 Editor::set_selection_from_punch()
1040 {
1041         Location* location;
1042
1043         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1044                 return;
1045         }
1046
1047         set_selection_from_range (*location);
1048 }
1049
1050 void
1051 Editor::set_selection_from_loop()
1052 {
1053         Location* location;
1054
1055         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1056                 return;
1057         }
1058         set_selection_from_range (*location);
1059 }
1060
1061 void
1062 Editor::set_selection_from_range (Location& loc)
1063 {
1064         begin_reversible_command (_("set selection from range"));
1065         selection->set (loc.start(), loc.end());
1066         commit_reversible_command ();
1067
1068         if (!Profile->get_sae()) {
1069                 set_mouse_mode (Editing::MouseRange, false);
1070         }
1071 }
1072
1073 void
1074 Editor::select_all_selectables_using_time_selection ()
1075 {
1076         list<Selectable *> touched;
1077
1078         if (selection->time.empty()) {
1079                 return;
1080         }
1081
1082         nframes64_t start = selection->time[clicked_selection].start;
1083         nframes64_t end = selection->time[clicked_selection].end;
1084
1085         if (end - start < 1)  {
1086                 return;
1087         }
1088
1089         TrackViewList* ts;
1090
1091         if (selection->tracks.empty()) {
1092                 ts = &track_views;
1093         } else {
1094                 ts = &selection->tracks;
1095         }
1096
1097         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1098                 if ((*iter)->hidden()) {
1099                         continue;
1100                 }
1101                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1102         }
1103
1104         begin_reversible_command (_("select all from range"));
1105         selection->set (touched);
1106         commit_reversible_command ();
1107 }
1108
1109
1110 void
1111 Editor::select_all_selectables_using_punch()
1112 {
1113         Location* location = _session->locations()->auto_punch_location();
1114         list<Selectable *> touched;
1115
1116         if (location == 0 || (location->end() - location->start() <= 1))  {
1117                 return;
1118         }
1119
1120
1121         TrackViewList* ts;
1122
1123         if (selection->tracks.empty()) {
1124                 ts = &track_views;
1125         } else {
1126                 ts = &selection->tracks;
1127         }
1128
1129         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1130                 if ((*iter)->hidden()) {
1131                         continue;
1132                 }
1133                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1134         }
1135         begin_reversible_command (_("select all from punch"));
1136         selection->set (touched);
1137         commit_reversible_command ();
1138
1139 }
1140
1141 void
1142 Editor::select_all_selectables_using_loop()
1143 {
1144         Location* location = _session->locations()->auto_loop_location();
1145         list<Selectable *> touched;
1146
1147         if (location == 0 || (location->end() - location->start() <= 1))  {
1148                 return;
1149         }
1150
1151
1152         TrackViewList* ts;
1153
1154         if (selection->tracks.empty()) {
1155                 ts = &track_views;
1156         } else {
1157                 ts = &selection->tracks;
1158         }
1159
1160         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1161                 if ((*iter)->hidden()) {
1162                         continue;
1163                 }
1164                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1165         }
1166         begin_reversible_command (_("select all from loop"));
1167         selection->set (touched);
1168         commit_reversible_command ();
1169
1170 }
1171
1172 void
1173 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1174 {
1175         nframes64_t start;
1176         nframes64_t end;
1177         list<Selectable *> touched;
1178
1179         if (after) {
1180                 begin_reversible_command (_("select all after cursor"));
1181                 start = cursor->current_frame ;
1182                 end = _session->current_end_frame();
1183         } else {
1184                 if (cursor->current_frame > 0) {
1185                         begin_reversible_command (_("select all before cursor"));
1186                         start = 0;
1187                         end = cursor->current_frame - 1;
1188                 } else {
1189                         return;
1190                 }
1191         }
1192
1193
1194         TrackViewList* ts;
1195
1196         if (selection->tracks.empty()) {
1197                 ts = &track_views;
1198         } else {
1199                 ts = &selection->tracks;
1200         }
1201
1202         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1203                 if ((*iter)->hidden()) {
1204                         continue;
1205                 }
1206                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1207         }
1208         selection->set (touched);
1209         commit_reversible_command ();
1210 }
1211
1212 void
1213 Editor::select_all_selectables_using_edit (bool after)
1214 {
1215         nframes64_t start;
1216         nframes64_t end;
1217         list<Selectable *> touched;
1218
1219         if (after) {
1220                 begin_reversible_command (_("select all after edit"));
1221                 start = get_preferred_edit_position();
1222                 end = _session->current_end_frame();
1223         } else {
1224                 if ((end = get_preferred_edit_position()) > 1) {
1225                         begin_reversible_command (_("select all before edit"));
1226                         start = 0;
1227                         end -= 1;
1228                 } else {
1229                         return;
1230                 }
1231         }
1232
1233
1234         TrackViewList* ts;
1235
1236         if (selection->tracks.empty()) {
1237                 ts = &track_views;
1238         } else {
1239                 ts = &selection->tracks;
1240         }
1241
1242         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1243                 if ((*iter)->hidden()) {
1244                         continue;
1245                 }
1246                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1247         }
1248         selection->set (touched);
1249         commit_reversible_command ();
1250 }
1251
1252 void
1253 Editor::select_all_selectables_between (bool /*within*/)
1254 {
1255         nframes64_t start;
1256         nframes64_t end;
1257         list<Selectable *> touched;
1258
1259         if (!get_edit_op_range (start, end)) {
1260                 return;
1261         }
1262
1263         TrackViewList* ts;
1264
1265         if (selection->tracks.empty()) {
1266                 ts = &track_views;
1267         } else {
1268                 ts = &selection->tracks;
1269         }
1270
1271         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1272                 if ((*iter)->hidden()) {
1273                         continue;
1274                 }
1275                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1276         }
1277
1278         selection->set (touched);
1279 }
1280
1281 void
1282 Editor::select_range_between ()
1283 {
1284         nframes64_t start;
1285         nframes64_t end;
1286
1287         if (!get_edit_op_range (start, end)) {
1288                 return;
1289         }
1290
1291         set_mouse_mode (MouseRange);
1292         selection->set (start, end);
1293 }
1294
1295 bool
1296 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1297 {
1298         nframes64_t m;
1299         bool ignored;
1300
1301         /* in range mode, use any existing selection */
1302
1303         if (mouse_mode == MouseRange && !selection->time.empty()) {
1304                 /* we know that these are ordered */
1305                 start = selection->time.start();
1306                 end = selection->time.end_frame();
1307                 return true;
1308         }
1309
1310         if (!mouse_frame (m, ignored)) {
1311                 /* mouse is not in a canvas, try playhead+selected marker.
1312                    this is probably most true when using menus.
1313                  */
1314
1315                 if (selection->markers.empty()) {
1316                         return false;
1317                 }
1318
1319                 start = selection->markers.front()->position();
1320                 end = _session->audible_frame();
1321
1322         } else {
1323
1324                 switch (_edit_point) {
1325                 case EditAtPlayhead:
1326                         if (selection->markers.empty()) {
1327                                 /* use mouse + playhead */
1328                                 start = m;
1329                                 end = _session->audible_frame();
1330                         } else {
1331                                 /* use playhead + selected marker */
1332                                 start = _session->audible_frame();
1333                                 end = selection->markers.front()->position();
1334                         }
1335                         break;
1336
1337                 case EditAtMouse:
1338                         /* use mouse + selected marker */
1339                         if (selection->markers.empty()) {
1340                                 start = m;
1341                                 end = _session->audible_frame();
1342                         } else {
1343                                 start = selection->markers.front()->position();
1344                                 end = m;
1345                         }
1346                         break;
1347
1348                 case EditAtSelectedMarker:
1349                         /* use mouse + selected marker */
1350                         if (selection->markers.empty()) {
1351
1352                                 MessageDialog win (_("No edit range defined"),
1353                                                    false,
1354                                                    MESSAGE_INFO,
1355                                                    BUTTONS_OK);
1356
1357                                 win.set_secondary_text (
1358                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1359
1360
1361                                 win.set_default_response (RESPONSE_CLOSE);
1362                                 win.set_position (Gtk::WIN_POS_MOUSE);
1363                                 win.show_all();
1364
1365                                 win.run ();
1366
1367                                 return false; // NO RANGE
1368                         }
1369                         start = selection->markers.front()->position();
1370                         end = m;
1371                         break;
1372                 }
1373         }
1374
1375         if (start == end) {
1376                 return false;
1377         }
1378
1379         if (start > end) {
1380                 swap (start, end);
1381         }
1382
1383         /* turn range into one delimited by start...end,
1384            not start...end-1
1385         */
1386
1387         end++;
1388
1389         return true;
1390 }
1391
1392 void
1393 Editor::deselect_all ()
1394 {
1395         selection->clear ();
1396 }
1397
1398 long
1399 Editor::select_range_around_region (RegionView* rv)
1400 {
1401         assert (rv);
1402         
1403         selection->set (&rv->get_time_axis_view());
1404         
1405         selection->time.clear ();
1406         boost::shared_ptr<Region> r = rv->region ();
1407         return selection->set (r->position(), r->position() + r->length());
1408 }