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