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