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