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