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