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