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