the Properties & 64bit region commit
[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, PBD::PropertyID 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->enabled_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, PBD::PropertyID property) const
357 {
358         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview(), property);
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, PBD::PropertyID 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, ARDOUR::Properties::select.id);
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, ARDOUR::Properties::select.id);
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         _regions->unselect_all ();
874
875         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
876                 (*i)->set_selected_regionviews (selection->regions);
877         }
878
879         _regions->set_selected (selection->regions);
880
881         sensitize_the_right_region_actions (!selection->regions.empty());
882
883         _regions->block_change_connection (false);
884         editor_regions_selection_changed_connection.block(false);
885 }
886
887 void
888 Editor::point_selection_changed ()
889 {
890         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
891                 (*i)->set_selected_points (selection->points);
892         }
893 }
894
895 void
896 Editor::select_all_in_track (Selection::Operation op)
897 {
898         list<Selectable *> touched;
899
900         if (!clicked_routeview) {
901                 return;
902         }
903
904         clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
905
906         switch (op) {
907         case Selection::Toggle:
908                 selection->add (touched);
909                 break;
910         case Selection::Set:
911                 selection->set (touched);
912                 break;
913         case Selection::Extend:
914                 /* meaningless, because we're selecting everything */
915                 break;
916         case Selection::Add:
917                 selection->add (touched);
918                 break;
919         }
920 }
921
922 void
923 Editor::select_all (Selection::Operation op)
924 {
925         list<Selectable *> touched;
926
927         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
928                 if ((*iter)->hidden()) {
929                         continue;
930                 }
931                 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
932         }
933         begin_reversible_command (_("select all"));
934         switch (op) {
935         case Selection::Add:
936                 selection->add (touched);
937                 break;
938         case Selection::Toggle:
939                 selection->add (touched);
940                 break;
941         case Selection::Set:
942                 selection->set (touched);
943                 break;
944         case Selection::Extend:
945                 /* meaningless, because we're selecting everything */
946                 break;
947         }
948         commit_reversible_command ();
949 }
950 void
951 Editor::invert_selection_in_track ()
952 {
953         list<Selectable *> touched;
954
955         if (!clicked_routeview) {
956                 return;
957         }
958
959         clicked_routeview->get_inverted_selectables (*selection, touched);
960         selection->set (touched);
961 }
962
963 void
964 Editor::invert_selection ()
965 {
966         list<Selectable *> touched;
967
968         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
969                 if ((*iter)->hidden()) {
970                         continue;
971                 }
972                 (*iter)->get_inverted_selectables (*selection, touched);
973         }
974
975         selection->set (touched);
976 }
977
978 /** @param top Top (lower) y limit in trackview coordinates.
979  *  @param bottom Bottom (higher) y limit in trackview coordinates.
980  */
981 bool
982 Editor::select_all_within (
983         nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op
984         )
985 {
986         list<Selectable*> found;
987         TrackViewList tracks;
988
989         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
990                 
991                 if ((*iter)->hidden()) {
992                         continue;
993                 }
994
995                 list<Selectable*>::size_type const n = found.size ();
996
997                 (*iter)->get_selectables (start, end, top, bot, found);
998
999                 if (n != found.size()) {
1000                         tracks.push_back (*iter);
1001                 }
1002         }
1003         
1004         if (found.empty()) {
1005                 return false;
1006         }
1007
1008         if (!tracks.empty()) {
1009
1010                 switch (op) {
1011                 case Selection::Add:
1012                         selection->add (tracks);
1013                         break;
1014                 case Selection::Toggle:
1015                         selection->toggle (tracks);
1016                         break;
1017                 case Selection::Set:
1018                         selection->set (tracks);
1019                         break;
1020                 case Selection::Extend:
1021                         /* not defined yet */
1022                         break;
1023                 }
1024         }
1025
1026         begin_reversible_command (_("select all within"));
1027         switch (op) {
1028         case Selection::Add:
1029                 selection->add (found);
1030                 break;
1031         case Selection::Toggle:
1032                 selection->toggle (found);
1033                 break;
1034         case Selection::Set:
1035                 selection->set (found);
1036                 break;
1037         case Selection::Extend:
1038                 /* not defined yet */
1039                 break;
1040         }
1041
1042         commit_reversible_command ();
1043
1044         return !found.empty();
1045 }
1046
1047 void
1048 Editor::set_selection_from_region ()
1049 {
1050         if (selection->regions.empty()) {
1051                 return;
1052         }
1053
1054         selection->set (selection->regions.start(), selection->regions.end_frame());
1055         if (!Profile->get_sae()) {
1056                 set_mouse_mode (Editing::MouseRange, false);
1057         }
1058 }
1059
1060 void
1061 Editor::set_selection_from_punch()
1062 {
1063         Location* location;
1064
1065         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1066                 return;
1067         }
1068
1069         set_selection_from_range (*location);
1070 }
1071
1072 void
1073 Editor::set_selection_from_loop()
1074 {
1075         Location* location;
1076
1077         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1078                 return;
1079         }
1080         set_selection_from_range (*location);
1081 }
1082
1083 void
1084 Editor::set_selection_from_range (Location& loc)
1085 {
1086         begin_reversible_command (_("set selection from range"));
1087         selection->set (loc.start(), loc.end());
1088         commit_reversible_command ();
1089
1090         if (!Profile->get_sae()) {
1091                 set_mouse_mode (Editing::MouseRange, false);
1092         }
1093 }
1094
1095 void
1096 Editor::select_all_selectables_using_time_selection ()
1097 {
1098         list<Selectable *> touched;
1099
1100         if (selection->time.empty()) {
1101                 return;
1102         }
1103
1104         nframes64_t start = selection->time[clicked_selection].start;
1105         nframes64_t end = selection->time[clicked_selection].end;
1106
1107         if (end - start < 1)  {
1108                 return;
1109         }
1110
1111         TrackViewList* ts;
1112
1113         if (selection->tracks.empty()) {
1114                 ts = &track_views;
1115         } else {
1116                 ts = &selection->tracks;
1117         }
1118
1119         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1120                 if ((*iter)->hidden()) {
1121                         continue;
1122                 }
1123                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1124         }
1125
1126         begin_reversible_command (_("select all from range"));
1127         selection->set (touched);
1128         commit_reversible_command ();
1129 }
1130
1131
1132 void
1133 Editor::select_all_selectables_using_punch()
1134 {
1135         Location* location = _session->locations()->auto_punch_location();
1136         list<Selectable *> touched;
1137
1138         if (location == 0 || (location->end() - location->start() <= 1))  {
1139                 return;
1140         }
1141
1142
1143         TrackViewList* ts;
1144
1145         if (selection->tracks.empty()) {
1146                 ts = &track_views;
1147         } else {
1148                 ts = &selection->tracks;
1149         }
1150
1151         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1152                 if ((*iter)->hidden()) {
1153                         continue;
1154                 }
1155                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1156         }
1157         begin_reversible_command (_("select all from punch"));
1158         selection->set (touched);
1159         commit_reversible_command ();
1160
1161 }
1162
1163 void
1164 Editor::select_all_selectables_using_loop()
1165 {
1166         Location* location = _session->locations()->auto_loop_location();
1167         list<Selectable *> touched;
1168
1169         if (location == 0 || (location->end() - location->start() <= 1))  {
1170                 return;
1171         }
1172
1173
1174         TrackViewList* ts;
1175
1176         if (selection->tracks.empty()) {
1177                 ts = &track_views;
1178         } else {
1179                 ts = &selection->tracks;
1180         }
1181
1182         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1183                 if ((*iter)->hidden()) {
1184                         continue;
1185                 }
1186                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1187         }
1188         begin_reversible_command (_("select all from loop"));
1189         selection->set (touched);
1190         commit_reversible_command ();
1191
1192 }
1193
1194 void
1195 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1196 {
1197         nframes64_t start;
1198         nframes64_t end;
1199         list<Selectable *> touched;
1200
1201         if (after) {
1202                 begin_reversible_command (_("select all after cursor"));
1203                 start = cursor->current_frame ;
1204                 end = _session->current_end_frame();
1205         } else {
1206                 if (cursor->current_frame > 0) {
1207                         begin_reversible_command (_("select all before cursor"));
1208                         start = 0;
1209                         end = cursor->current_frame - 1;
1210                 } else {
1211                         return;
1212                 }
1213         }
1214
1215
1216         TrackViewList* ts;
1217
1218         if (selection->tracks.empty()) {
1219                 ts = &track_views;
1220         } else {
1221                 ts = &selection->tracks;
1222         }
1223
1224         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1225                 if ((*iter)->hidden()) {
1226                         continue;
1227                 }
1228                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1229         }
1230         selection->set (touched);
1231         commit_reversible_command ();
1232 }
1233
1234 void
1235 Editor::select_all_selectables_using_edit (bool after)
1236 {
1237         nframes64_t start;
1238         nframes64_t end;
1239         list<Selectable *> touched;
1240
1241         if (after) {
1242                 begin_reversible_command (_("select all after edit"));
1243                 start = get_preferred_edit_position();
1244                 end = _session->current_end_frame();
1245         } else {
1246                 if ((end = get_preferred_edit_position()) > 1) {
1247                         begin_reversible_command (_("select all before edit"));
1248                         start = 0;
1249                         end -= 1;
1250                 } else {
1251                         return;
1252                 }
1253         }
1254
1255
1256         TrackViewList* ts;
1257
1258         if (selection->tracks.empty()) {
1259                 ts = &track_views;
1260         } else {
1261                 ts = &selection->tracks;
1262         }
1263
1264         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1265                 if ((*iter)->hidden()) {
1266                         continue;
1267                 }
1268                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1269         }
1270         selection->set (touched);
1271         commit_reversible_command ();
1272 }
1273
1274 void
1275 Editor::select_all_selectables_between (bool /*within*/)
1276 {
1277         nframes64_t start;
1278         nframes64_t end;
1279         list<Selectable *> touched;
1280
1281         if (!get_edit_op_range (start, end)) {
1282                 return;
1283         }
1284
1285         TrackViewList* ts;
1286
1287         if (selection->tracks.empty()) {
1288                 ts = &track_views;
1289         } else {
1290                 ts = &selection->tracks;
1291         }
1292
1293         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1294                 if ((*iter)->hidden()) {
1295                         continue;
1296                 }
1297                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1298         }
1299
1300         selection->set (touched);
1301 }
1302
1303 void
1304 Editor::select_range_between ()
1305 {
1306         nframes64_t start;
1307         nframes64_t end;
1308
1309         if (!get_edit_op_range (start, end)) {
1310                 return;
1311         }
1312
1313         set_mouse_mode (MouseRange);
1314         selection->set (start, end);
1315 }
1316
1317 bool
1318 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1319 {
1320         nframes64_t m;
1321         bool ignored;
1322
1323         /* in range mode, use any existing selection */
1324
1325         if (mouse_mode == MouseRange && !selection->time.empty()) {
1326                 /* we know that these are ordered */
1327                 start = selection->time.start();
1328                 end = selection->time.end_frame();
1329                 return true;
1330         }
1331
1332         if (!mouse_frame (m, ignored)) {
1333                 /* mouse is not in a canvas, try playhead+selected marker.
1334                    this is probably most true when using menus.
1335                  */
1336
1337                 if (selection->markers.empty()) {
1338                         return false;
1339                 }
1340
1341                 start = selection->markers.front()->position();
1342                 end = _session->audible_frame();
1343
1344         } else {
1345
1346                 switch (_edit_point) {
1347                 case EditAtPlayhead:
1348                         if (selection->markers.empty()) {
1349                                 /* use mouse + playhead */
1350                                 start = m;
1351                                 end = _session->audible_frame();
1352                         } else {
1353                                 /* use playhead + selected marker */
1354                                 start = _session->audible_frame();
1355                                 end = selection->markers.front()->position();
1356                         }
1357                         break;
1358
1359                 case EditAtMouse:
1360                         /* use mouse + selected marker */
1361                         if (selection->markers.empty()) {
1362                                 start = m;
1363                                 end = _session->audible_frame();
1364                         } else {
1365                                 start = selection->markers.front()->position();
1366                                 end = m;
1367                         }
1368                         break;
1369
1370                 case EditAtSelectedMarker:
1371                         /* use mouse + selected marker */
1372                         if (selection->markers.empty()) {
1373
1374                                 MessageDialog win (_("No edit range defined"),
1375                                                    false,
1376                                                    MESSAGE_INFO,
1377                                                    BUTTONS_OK);
1378
1379                                 win.set_secondary_text (
1380                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1381
1382
1383                                 win.set_default_response (RESPONSE_CLOSE);
1384                                 win.set_position (Gtk::WIN_POS_MOUSE);
1385                                 win.show_all();
1386
1387                                 win.run ();
1388
1389                                 return false; // NO RANGE
1390                         }
1391                         start = selection->markers.front()->position();
1392                         end = m;
1393                         break;
1394                 }
1395         }
1396
1397         if (start == end) {
1398                 return false;
1399         }
1400
1401         if (start > end) {
1402                 swap (start, end);
1403         }
1404
1405         /* turn range into one delimited by start...end,
1406            not start...end-1
1407         */
1408
1409         end++;
1410
1411         return true;
1412 }
1413
1414 void
1415 Editor::deselect_all ()
1416 {
1417         selection->clear ();
1418 }
1419
1420 long
1421 Editor::select_range_around_region (RegionView* rv)
1422 {
1423         assert (rv);
1424         
1425         selection->set (&rv->get_time_axis_view());
1426         
1427         selection->time.clear ();
1428         boost::shared_ptr<Region> r = rv->region ();
1429         return selection->set (r->position(), r->position() + r->length());
1430 }