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