label various spinners in the step editor
[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 we're adding to the selection and all of the selectables
980  *  within the region are already selected.
981  */
982 bool
983 Editor::select_all_within (
984         framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected
985         )
986 {
987         list<Selectable*> found;
988
989         for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
990                 
991                 if ((*iter)->hidden()) {
992                         continue;
993                 }
994
995                 (*iter)->get_selectables (start, end, top, bot, found);
996         }
997
998         if (found.empty()) {
999                 return false;
1000         }
1001
1002         if (preserve_if_selected && op != Selection::Toggle) {
1003                 list<Selectable*>::iterator i = found.begin();
1004                 while (i != found.end() && (*i)->get_selected()) {
1005                         ++i;
1006                 }
1007
1008                 if (i == found.end()) {
1009                         return false;
1010                 }
1011         }
1012
1013         begin_reversible_command (_("select all within"));
1014         switch (op) {
1015         case Selection::Add:
1016                 selection->add (found);
1017                 break;
1018         case Selection::Toggle:
1019                 selection->toggle (found);
1020                 break;
1021         case Selection::Set:
1022                 selection->set (found);
1023                 break;
1024         case Selection::Extend:
1025                 /* not defined yet */
1026                 break;
1027         }
1028
1029         commit_reversible_command ();
1030
1031         return !found.empty();
1032 }
1033
1034 void
1035 Editor::set_selection_from_region ()
1036 {
1037         if (selection->regions.empty()) {
1038                 return;
1039         }
1040
1041         selection->set (selection->regions.start(), selection->regions.end_frame());
1042         if (!Profile->get_sae()) {
1043                 set_mouse_mode (Editing::MouseRange, false);
1044         }
1045 }
1046
1047 void
1048 Editor::set_selection_from_punch()
1049 {
1050         Location* location;
1051
1052         if ((location = _session->locations()->auto_punch_location()) == 0)  {
1053                 return;
1054         }
1055
1056         set_selection_from_range (*location);
1057 }
1058
1059 void
1060 Editor::set_selection_from_loop()
1061 {
1062         Location* location;
1063
1064         if ((location = _session->locations()->auto_loop_location()) == 0)  {
1065                 return;
1066         }
1067         set_selection_from_range (*location);
1068 }
1069
1070 void
1071 Editor::set_selection_from_range (Location& loc)
1072 {
1073         begin_reversible_command (_("set selection from range"));
1074         selection->set (loc.start(), loc.end());
1075         commit_reversible_command ();
1076
1077         if (!Profile->get_sae()) {
1078                 set_mouse_mode (Editing::MouseRange, false);
1079         }
1080 }
1081
1082 void
1083 Editor::select_all_selectables_using_time_selection ()
1084 {
1085         list<Selectable *> touched;
1086
1087         if (selection->time.empty()) {
1088                 return;
1089         }
1090
1091         nframes64_t start = selection->time[clicked_selection].start;
1092         nframes64_t end = selection->time[clicked_selection].end;
1093
1094         if (end - start < 1)  {
1095                 return;
1096         }
1097
1098         TrackViewList* ts;
1099
1100         if (selection->tracks.empty()) {
1101                 ts = &track_views;
1102         } else {
1103                 ts = &selection->tracks;
1104         }
1105
1106         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1107                 if ((*iter)->hidden()) {
1108                         continue;
1109                 }
1110                 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1111         }
1112
1113         begin_reversible_command (_("select all from range"));
1114         selection->set (touched);
1115         commit_reversible_command ();
1116 }
1117
1118
1119 void
1120 Editor::select_all_selectables_using_punch()
1121 {
1122         Location* location = _session->locations()->auto_punch_location();
1123         list<Selectable *> touched;
1124
1125         if (location == 0 || (location->end() - location->start() <= 1))  {
1126                 return;
1127         }
1128
1129
1130         TrackViewList* ts;
1131
1132         if (selection->tracks.empty()) {
1133                 ts = &track_views;
1134         } else {
1135                 ts = &selection->tracks;
1136         }
1137
1138         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1139                 if ((*iter)->hidden()) {
1140                         continue;
1141                 }
1142                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1143         }
1144         begin_reversible_command (_("select all from punch"));
1145         selection->set (touched);
1146         commit_reversible_command ();
1147
1148 }
1149
1150 void
1151 Editor::select_all_selectables_using_loop()
1152 {
1153         Location* location = _session->locations()->auto_loop_location();
1154         list<Selectable *> touched;
1155
1156         if (location == 0 || (location->end() - location->start() <= 1))  {
1157                 return;
1158         }
1159
1160
1161         TrackViewList* ts;
1162
1163         if (selection->tracks.empty()) {
1164                 ts = &track_views;
1165         } else {
1166                 ts = &selection->tracks;
1167         }
1168
1169         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1170                 if ((*iter)->hidden()) {
1171                         continue;
1172                 }
1173                 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1174         }
1175         begin_reversible_command (_("select all from loop"));
1176         selection->set (touched);
1177         commit_reversible_command ();
1178
1179 }
1180
1181 void
1182 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1183 {
1184         nframes64_t start;
1185         nframes64_t end;
1186         list<Selectable *> touched;
1187
1188         if (after) {
1189                 begin_reversible_command (_("select all after cursor"));
1190                 start = cursor->current_frame ;
1191                 end = _session->current_end_frame();
1192         } else {
1193                 if (cursor->current_frame > 0) {
1194                         begin_reversible_command (_("select all before cursor"));
1195                         start = 0;
1196                         end = cursor->current_frame - 1;
1197                 } else {
1198                         return;
1199                 }
1200         }
1201
1202
1203         TrackViewList* ts;
1204
1205         if (selection->tracks.empty()) {
1206                 ts = &track_views;
1207         } else {
1208                 ts = &selection->tracks;
1209         }
1210
1211         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1212                 if ((*iter)->hidden()) {
1213                         continue;
1214                 }
1215                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1216         }
1217         selection->set (touched);
1218         commit_reversible_command ();
1219 }
1220
1221 void
1222 Editor::select_all_selectables_using_edit (bool after)
1223 {
1224         nframes64_t start;
1225         nframes64_t end;
1226         list<Selectable *> touched;
1227
1228         if (after) {
1229                 begin_reversible_command (_("select all after edit"));
1230                 start = get_preferred_edit_position();
1231                 end = _session->current_end_frame();
1232         } else {
1233                 if ((end = get_preferred_edit_position()) > 1) {
1234                         begin_reversible_command (_("select all before edit"));
1235                         start = 0;
1236                         end -= 1;
1237                 } else {
1238                         return;
1239                 }
1240         }
1241
1242
1243         TrackViewList* ts;
1244
1245         if (selection->tracks.empty()) {
1246                 ts = &track_views;
1247         } else {
1248                 ts = &selection->tracks;
1249         }
1250
1251         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1252                 if ((*iter)->hidden()) {
1253                         continue;
1254                 }
1255                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1256         }
1257         selection->set (touched);
1258         commit_reversible_command ();
1259 }
1260
1261 void
1262 Editor::select_all_selectables_between (bool /*within*/)
1263 {
1264         nframes64_t start;
1265         nframes64_t end;
1266         list<Selectable *> touched;
1267
1268         if (!get_edit_op_range (start, end)) {
1269                 return;
1270         }
1271
1272         TrackViewList* ts;
1273
1274         if (selection->tracks.empty()) {
1275                 ts = &track_views;
1276         } else {
1277                 ts = &selection->tracks;
1278         }
1279
1280         for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1281                 if ((*iter)->hidden()) {
1282                         continue;
1283                 }
1284                 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1285         }
1286
1287         selection->set (touched);
1288 }
1289
1290 void
1291 Editor::select_range_between ()
1292 {
1293         nframes64_t start;
1294         nframes64_t end;
1295
1296         if (!get_edit_op_range (start, end)) {
1297                 return;
1298         }
1299
1300         set_mouse_mode (MouseRange);
1301         selection->set (start, end);
1302 }
1303
1304 bool
1305 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1306 {
1307         nframes64_t m;
1308         bool ignored;
1309
1310         /* in range mode, use any existing selection */
1311
1312         if (mouse_mode == MouseRange && !selection->time.empty()) {
1313                 /* we know that these are ordered */
1314                 start = selection->time.start();
1315                 end = selection->time.end_frame();
1316                 return true;
1317         }
1318
1319         if (!mouse_frame (m, ignored)) {
1320                 /* mouse is not in a canvas, try playhead+selected marker.
1321                    this is probably most true when using menus.
1322                  */
1323
1324                 if (selection->markers.empty()) {
1325                         return false;
1326                 }
1327
1328                 start = selection->markers.front()->position();
1329                 end = _session->audible_frame();
1330
1331         } else {
1332
1333                 switch (_edit_point) {
1334                 case EditAtPlayhead:
1335                         if (selection->markers.empty()) {
1336                                 /* use mouse + playhead */
1337                                 start = m;
1338                                 end = _session->audible_frame();
1339                         } else {
1340                                 /* use playhead + selected marker */
1341                                 start = _session->audible_frame();
1342                                 end = selection->markers.front()->position();
1343                         }
1344                         break;
1345
1346                 case EditAtMouse:
1347                         /* use mouse + selected marker */
1348                         if (selection->markers.empty()) {
1349                                 start = m;
1350                                 end = _session->audible_frame();
1351                         } else {
1352                                 start = selection->markers.front()->position();
1353                                 end = m;
1354                         }
1355                         break;
1356
1357                 case EditAtSelectedMarker:
1358                         /* use mouse + selected marker */
1359                         if (selection->markers.empty()) {
1360
1361                                 MessageDialog win (_("No edit range defined"),
1362                                                    false,
1363                                                    MESSAGE_INFO,
1364                                                    BUTTONS_OK);
1365
1366                                 win.set_secondary_text (
1367                                         _("the edit point is Selected Marker\nbut there is no selected marker."));
1368
1369
1370                                 win.set_default_response (RESPONSE_CLOSE);
1371                                 win.set_position (Gtk::WIN_POS_MOUSE);
1372                                 win.show_all();
1373
1374                                 win.run ();
1375
1376                                 return false; // NO RANGE
1377                         }
1378                         start = selection->markers.front()->position();
1379                         end = m;
1380                         break;
1381                 }
1382         }
1383
1384         if (start == end) {
1385                 return false;
1386         }
1387
1388         if (start > end) {
1389                 swap (start, end);
1390         }
1391
1392         /* turn range into one delimited by start...end,
1393            not start...end-1
1394         */
1395
1396         end++;
1397
1398         return true;
1399 }
1400
1401 void
1402 Editor::deselect_all ()
1403 {
1404         selection->clear ();
1405 }
1406
1407 long
1408 Editor::select_range_around_region (RegionView* rv)
1409 {
1410         assert (rv);
1411         
1412         selection->set (&rv->get_time_axis_view());
1413         
1414         selection->time.clear ();
1415         boost::shared_ptr<Region> r = rv->region ();
1416         return selection->set (r->position(), r->position() + r->length());
1417 }