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