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