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