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