revert VST debugging hacks
[ardour2.git] / gtk2_ardour / editor_selection.cc
blobe6a0695bda4fdd11d016650f1b16f8dc5d70f678
1 /*
2 Copyright (C) 2000-2006 Paul Davis
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.
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.
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.
20 #include <algorithm>
21 #include <cstdlib>
23 #include <pbd/stacktrace.h>
25 #include <ardour/diskstream.h>
26 #include <ardour/playlist.h>
27 #include <ardour/route_group.h>
28 #include <ardour/profile.h>
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"
37 #include "i18n.h"
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;
48 struct TrackViewByPositionSorter
50 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
51 return a->y_position < b->y_position;
55 bool
56 Editor::extend_selection_to_track (TimeAxisView& view)
58 if (selection->selected (&view)) {
59 /* already selected, do nothing */
60 return false;
63 if (selection->tracks.empty()) {
65 if (!selection->selected (&view)) {
66 selection->set (&view);
67 return true;
68 } else {
69 return false;
73 /* something is already selected, so figure out which range of things to add */
75 TrackViewList to_be_added;
76 TrackViewList sorted = track_views;
77 TrackViewByPositionSorter cmp;
78 bool passed_clicked = false;
79 bool forwards = true;
81 sorted.sort (cmp);
83 if (!selection->selected (&view)) {
84 to_be_added.push_back (&view);
87 /* figure out if we should go forward or backwards */
89 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
91 if ((*i) == &view) {
92 passed_clicked = true;
95 if (selection->selected (*i)) {
96 if (passed_clicked) {
97 forwards = true;
98 } else {
99 forwards = false;
101 break;
105 passed_clicked = false;
107 if (forwards) {
109 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
111 if ((*i) == &view) {
112 passed_clicked = true;
113 continue;
116 if (passed_clicked) {
117 if ((*i)->hidden()) {
118 continue;
120 if (selection->selected (*i)) {
121 break;
122 } else if (!(*i)->hidden()) {
123 to_be_added.push_back (*i);
128 } else {
130 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
132 if ((*r) == &view) {
133 passed_clicked = true;
134 continue;
137 if (passed_clicked) {
139 if ((*r)->hidden()) {
140 continue;
143 if (selection->selected (*r)) {
144 break;
145 } else if (!(*r)->hidden()) {
146 to_be_added.push_back (*r);
152 if (!to_be_added.empty()) {
153 selection->add (to_be_added);
154 return true;
157 return false;
160 void
161 Editor::select_all_tracks ()
163 TrackViewList visible_views;
164 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
165 if ((*i)->marked_for_display()) {
166 visible_views.push_back (*i);
169 selection->set (visible_views);
172 void
173 Editor::set_selected_track_as_side_effect (bool force)
175 if (!clicked_trackview) {
176 return;
179 if (!selection->tracks.empty()) {
181 if (!selection->selected (clicked_trackview)) {
182 selection->add (clicked_trackview);
185 } else if (force) {
186 selection->set (clicked_trackview);
190 void
191 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
193 switch (op) {
194 case Selection::Toggle:
195 if (selection->selected (&view)) {
196 if (!no_remove) {
197 selection->remove (&view);
199 } else {
200 selection->add (&view);
202 break;
204 case Selection::Add:
205 if (!selection->selected (&view)) {
206 selection->add (&view);
208 break;
210 case Selection::Set:
211 selection->set (&view);
212 break;
214 case Selection::Extend:
215 extend_selection_to_track (view);
216 break;
220 void
221 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
223 if (!clicked_trackview) {
224 return;
227 if (!press) {
228 return;
231 set_selected_track (*clicked_trackview, op, no_remove);
234 bool
235 Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove)
237 if (!clicked_control_point) {
238 return false;
241 /* select this point and any others that it represents */
243 double y1, y2;
244 nframes64_t x1, x2;
246 x1 = pixel_to_frame (clicked_control_point->get_x() - 10);
247 x2 = pixel_to_frame (clicked_control_point->get_x() + 10);
248 y1 = clicked_control_point->get_x() - 10;
249 y2 = clicked_control_point->get_y() + 10;
251 return select_all_within (x1, x2, y1, y2, selection->tracks, op);
254 void
255 Editor::get_onscreen_tracks (TrackViewList& tvl)
257 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
258 if ((*i)->y_position < canvas_height) {
259 tvl.push_back (*i);
264 void
265 Editor::get_relevant_audio_tracks (set<AudioTimeAxisView*>& relevant_tracks)
267 /* step one: get all selected tracks and all tracks in the relevant edit groups */
269 for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) {
271 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*ti);
273 if (!atv) {
274 continue;
277 RouteGroup* group = atv->route()->edit_group();
279 if (group && group->is_active()) {
281 /* active group for this track, loop over all tracks and get every member of the group */
283 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
285 AudioTimeAxisView* tatv;
287 if ((tatv = dynamic_cast<AudioTimeAxisView*> (*i)) != 0) {
289 if (tatv->route()->edit_group() == group) {
290 relevant_tracks.insert (tatv);
294 } else {
295 relevant_tracks.insert (atv);
302 * Call a slot for a given `basis' track and also for any track that is in the same
303 * active edit group.
304 * @param sl Slot to call.
305 * @param basis Basis track.
308 void
309 Editor::mapover_audio_tracks (slot<void,AudioTimeAxisView&,uint32_t> sl, TimeAxisView* basis)
311 AudioTimeAxisView* audio_basis = dynamic_cast<AudioTimeAxisView*> (basis);
312 if (audio_basis == 0) {
313 return;
316 /* work out the tracks that we will call the slot for; use
317 a set here as it will disallow possible duplicates of the
318 basis track */
319 set<AudioTimeAxisView*> tracks;
321 /* always call for the basis */
322 tracks.insert (audio_basis);
324 RouteGroup* group = audio_basis->route()->edit_group();
325 if (group && group->is_active()) {
327 /* the basis is a member of an active edit group; find other members */
328 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
329 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*> (*i);
330 if (v && v->route()->edit_group() == group) {
331 tracks.insert (v);
336 /* call the slots */
337 uint32_t const sz = tracks.size ();
338 for (set<AudioTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
339 sl (**i, sz);
343 void
344 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t ignored, RegionView* basis, vector<RegionView*>* all_equivs)
346 boost::shared_ptr<Playlist> pl;
347 vector<boost::shared_ptr<Region> > results;
348 RegionView* marv;
349 boost::shared_ptr<Diskstream> ds;
351 if ((ds = tv.get_diskstream()) == 0) {
352 /* bus */
353 return;
356 if (&tv == &basis->get_time_axis_view()) {
357 /* looking in same track as the original */
358 return;
361 if ((pl = ds->playlist()) != 0) {
362 pl->get_equivalent_regions (basis->region(), results);
365 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
366 if ((marv = tv.view()->find_view (*ir)) != 0) {
367 all_equivs->push_back (marv);
372 void
373 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions)
375 mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview());
377 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
379 equivalent_regions.push_back (basis);
382 bool
383 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove)
385 vector<RegionView*> all_equivalent_regions;
386 bool commit = false;
388 if (!clicked_regionview || !clicked_audio_trackview) {
389 return false;
392 if (press) {
393 button_release_can_deselect = false;
396 if (op == Selection::Toggle || op == Selection::Set) {
399 switch (op) {
400 case Selection::Toggle:
402 if (selection->selected (clicked_regionview)) {
403 if (press) {
405 /* whatever was clicked was selected already; do nothing here but allow
406 the button release to deselect it
409 button_release_can_deselect = true;
411 } else {
413 if (button_release_can_deselect) {
415 /* just remove this one region, but only on a permitted button release */
417 selection->remove (clicked_regionview);
418 commit = true;
420 /* no more deselect action on button release till a new press
421 finds an already selected object.
424 button_release_can_deselect = false;
428 } else {
430 if (press) {
432 if (selection->selected (clicked_audio_trackview)) {
433 get_equivalent_regions (clicked_regionview, all_equivalent_regions);
434 } else {
435 all_equivalent_regions.push_back (clicked_regionview);
438 /* add all the equivalent regions, but only on button press */
442 if (!all_equivalent_regions.empty()) {
443 commit = true;
446 selection->add (all_equivalent_regions);
449 break;
451 case Selection::Set:
452 if (!selection->selected (clicked_regionview)) {
454 get_equivalent_regions (clicked_regionview, all_equivalent_regions);
455 selection->set (all_equivalent_regions);
456 commit = true;
457 } else {
458 /* no commit necessary: clicked on an already selected region */
459 goto out;
461 break;
463 default:
464 /* silly compiler */
465 break;
468 } else if (op == Selection::Extend) {
470 list<Selectable*> results;
471 nframes64_t last_frame;
472 nframes64_t first_frame;
473 bool same_track = false;
475 /* 1. find the last selected regionview in the track that was clicked in */
477 last_frame = 0;
478 first_frame = max_frames;
480 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
481 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
483 if ((*x)->region()->last_frame() > last_frame) {
484 last_frame = (*x)->region()->last_frame();
487 if ((*x)->region()->first_frame() < first_frame) {
488 first_frame = (*x)->region()->first_frame();
491 same_track = true;
495 if (same_track) {
497 /* 2. figure out the boundaries for our search for new objects */
499 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
500 case OverlapNone:
501 if (last_frame < clicked_regionview->region()->first_frame()) {
502 first_frame = last_frame;
503 last_frame = clicked_regionview->region()->last_frame();
504 } else {
505 last_frame = first_frame;
506 first_frame = clicked_regionview->region()->first_frame();
508 break;
510 case OverlapExternal:
511 if (last_frame < clicked_regionview->region()->first_frame()) {
512 first_frame = last_frame;
513 last_frame = clicked_regionview->region()->last_frame();
514 } else {
515 last_frame = first_frame;
516 first_frame = clicked_regionview->region()->first_frame();
518 break;
520 case OverlapInternal:
521 if (last_frame < clicked_regionview->region()->first_frame()) {
522 first_frame = last_frame;
523 last_frame = clicked_regionview->region()->last_frame();
524 } else {
525 last_frame = first_frame;
526 first_frame = clicked_regionview->region()->first_frame();
528 break;
530 case OverlapStart:
531 case OverlapEnd:
532 /* nothing to do except add clicked region to selection, since it
533 overlaps with the existing selection in this track.
535 break;
538 } else {
540 /* click in a track that has no regions selected, so extend vertically
541 to pick out all regions that are defined by the existing selection
542 plus this one.
546 first_frame = entered_regionview->region()->position();
547 last_frame = entered_regionview->region()->last_frame();
549 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
550 if ((*i)->region()->position() < first_frame) {
551 first_frame = (*i)->region()->position();
553 if ((*i)->region()->last_frame() + 1 > last_frame) {
554 last_frame = (*i)->region()->last_frame();
559 /* 2. find all the tracks we should select in */
561 set<AudioTimeAxisView*> relevant_tracks;
562 set<AudioTimeAxisView*> already_in_selection;
564 get_relevant_audio_tracks (relevant_tracks);
566 if (relevant_tracks.empty()) {
568 /* no relevant tracks -> no tracks selected .. thus .. if
569 the regionview we're in isn't selected (i.e. we're
570 about to extend to it), then find all tracks between
571 the this one and any selected ones.
574 if (!selection->selected (entered_regionview)) {
576 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (&entered_regionview->get_time_axis_view());
578 if (atv) {
580 /* add this track to the ones we will search */
582 relevant_tracks.insert (atv);
584 /* find the track closest to this one that
585 already a selected region.
588 AudioTimeAxisView* closest = 0;
589 int distance = INT_MAX;
590 int key = atv->route()->order_key ("editor");
592 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
594 AudioTimeAxisView* aatv = dynamic_cast<AudioTimeAxisView*>(&(*x)->get_time_axis_view());
596 if (aatv && aatv != atv) {
598 pair<set<AudioTimeAxisView*>::iterator,bool> result;
600 result = already_in_selection.insert (aatv);
602 if (result.second) {
603 /* newly added to already_in_selection */
606 int d = aatv->route()->order_key ("editor");
608 d -= key;
610 if (abs (d) < distance) {
611 distance = abs (d);
612 closest = aatv;
618 if (closest) {
620 /* now add all tracks between that one and this one */
622 int okey = closest->route()->order_key ("editor");
624 if (okey > key) {
625 swap (okey, key);
628 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
629 AudioTimeAxisView* aatv = dynamic_cast<AudioTimeAxisView*>(*x);
630 if (aatv && aatv != atv) {
632 int k = aatv->route()->order_key ("editor");
634 if (k >= okey && k <= key) {
636 /* in range but don't add it if
637 it already has tracks selected.
638 this avoids odd selection
639 behaviour that feels wrong.
642 if (find (already_in_selection.begin(),
643 already_in_selection.end(),
644 aatv) == already_in_selection.end()) {
646 relevant_tracks.insert (aatv);
656 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
657 one that was clicked.
660 for (set<AudioTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
661 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
664 /* 4. convert to a vector of audio regions */
666 vector<RegionView*> regions;
668 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
669 RegionView* arv;
671 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
672 regions.push_back (arv);
676 if (!regions.empty()) {
677 selection->add (regions);
678 commit = true;
682 out:
683 return commit;
687 void
688 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
690 vector<RegionView*> all_equivalent_regions;
692 get_regions_corresponding_to (region, all_equivalent_regions);
694 if (all_equivalent_regions.empty()) {
695 return;
698 switch (op) {
699 case Selection::Toggle:
700 /* XXX this is not correct */
701 selection->toggle (all_equivalent_regions);
702 break;
703 case Selection::Set:
704 selection->set (all_equivalent_regions);
705 break;
706 case Selection::Extend:
707 selection->add (all_equivalent_regions);
708 break;
709 case Selection::Add:
710 selection->add (all_equivalent_regions);
711 break;
715 bool
716 Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr<Region> weak_r)
718 RegionView* rv;
719 boost::shared_ptr<Region> r (weak_r.lock());
721 if (!r) {
722 return true;
725 boost::shared_ptr<AudioRegion> ar;
727 if ((ar = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
728 return true;
731 if ((rv = sv->find_view (ar)) == 0) {
732 return true;
735 /* don't reset the selection if its something other than
736 a single other region.
739 if (selection->regions.size() > 1) {
740 return true;
743 begin_reversible_command (_("set selected regions"));
745 selection->set (rv);
747 commit_reversible_command () ;
749 return true;
752 void
753 Editor::track_selection_changed ()
755 switch (selection->tracks.size()){
756 case 0:
757 break;
758 default:
759 set_selected_mixer_strip (*(selection->tracks.front()));
760 break;
763 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
764 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
765 (*i)->set_selected (true);
766 } else {
767 (*i)->set_selected (false);
771 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
774 void
775 Editor::time_selection_changed ()
777 if (Profile->get_sae()) {
778 return;
781 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
782 (*i)->hide_selection ();
785 if (selection->tracks.empty()) {
786 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
787 (*i)->show_selection (selection->time);
789 } else {
790 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
791 (*i)->show_selection (selection->time);
795 if (selection->time.empty()) {
796 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
797 } else {
798 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
803 void
804 Editor::sensitize_the_right_region_actions (bool have_selected_regions)
806 for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
807 x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
809 string accel_path = (*x)->get_accel_path ();
810 AccelKey key;
812 /* if there is an accelerator, it should always be sensitive
813 to allow for keyboard ops on entered regions.
816 bool known = ActionManager::lookup_entry (accel_path, key);
818 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
819 (*x)->set_sensitive (true);
820 } else {
821 (*x)->set_sensitive (have_selected_regions);
827 void
828 Editor::region_selection_changed ()
830 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
831 (*i)->set_selected_regionviews (selection->regions);
834 sensitize_the_right_region_actions (!selection->regions.empty());
836 zoomed_to_region = false;
839 void
840 Editor::point_selection_changed ()
842 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
843 (*i)->set_selected_points (selection->points);
847 void
848 Editor::select_all_in_track (Selection::Operation op)
850 list<Selectable *> touched;
852 if (!clicked_trackview) {
853 return;
856 clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
858 switch (op) {
859 case Selection::Toggle:
860 selection->add (touched);
861 break;
862 case Selection::Set:
863 selection->set (touched);
864 break;
865 case Selection::Extend:
866 /* meaningless, because we're selecting everything */
867 break;
868 case Selection::Add:
869 selection->add (touched);
870 break;
874 void
875 Editor::select_all (Selection::Operation op)
877 list<Selectable *> touched;
879 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
880 if ((*iter)->hidden()) {
881 continue;
883 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
885 begin_reversible_command (_("select all"));
886 switch (op) {
887 case Selection::Add:
888 selection->add (touched);
889 break;
890 case Selection::Toggle:
891 selection->add (touched);
892 break;
893 case Selection::Set:
894 selection->set (touched);
895 break;
896 case Selection::Extend:
897 /* meaningless, because we're selecting everything */
898 break;
900 commit_reversible_command ();
903 void
904 Editor::invert_selection_in_track ()
906 list<Selectable *> touched;
908 if (!clicked_trackview) {
909 return;
912 clicked_trackview->get_inverted_selectables (*selection, touched);
913 selection->set (touched);
916 void
917 Editor::invert_selection ()
919 list<Selectable *> touched;
921 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
922 if ((*iter)->hidden()) {
923 continue;
925 (*iter)->get_inverted_selectables (*selection, touched);
928 selection->set (touched);
931 bool
932 Editor::select_all_within (nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
934 list<Selectable*> touched;
935 list<Selectable*>::size_type n = 0;
936 TrackViewList touched_tracks;
938 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
939 if ((*iter)->hidden()) {
940 continue;
943 n = touched.size();
945 (*iter)->get_selectables (start, end, top, bot, touched);
947 if (n != touched.size()) {
948 touched_tracks.push_back (*iter);
952 if (touched.empty()) {
953 return false;
956 if (!touched_tracks.empty()) {
958 switch (op) {
959 case Selection::Add:
960 selection->add (touched_tracks);
961 break;
962 case Selection::Toggle:
963 selection->toggle (touched_tracks);
964 break;
965 case Selection::Set:
966 selection->set (touched_tracks);
967 break;
968 case Selection::Extend:
969 /* not defined yet */
970 break;
974 begin_reversible_command (_("select all within"));
975 switch (op) {
976 case Selection::Add:
977 selection->add (touched);
978 break;
979 case Selection::Toggle:
980 selection->toggle (touched);
981 break;
982 case Selection::Set:
983 selection->set (touched);
984 break;
985 case Selection::Extend:
986 /* not defined yet */
987 break;
990 commit_reversible_command ();
992 return !touched.empty();
995 void
996 Editor::set_selection_from_audio_region ()
998 if (selection->regions.empty()) {
999 return;
1002 selection->set (0, selection->regions.start(), selection->regions.end_frame());
1003 if (!Profile->get_sae()) {
1004 set_mouse_mode (Editing::MouseRange, false);
1008 void
1009 Editor::set_selection_from_punch()
1011 Location* location;
1013 if ((location = session->locations()->auto_punch_location()) == 0) {
1014 return;
1017 set_selection_from_range (*location);
1020 void
1021 Editor::set_selection_from_loop()
1023 Location* location;
1025 if ((location = session->locations()->auto_loop_location()) == 0) {
1026 return;
1028 set_selection_from_range (*location);
1031 void
1032 Editor::set_selection_from_range (Location& loc)
1034 begin_reversible_command (_("set selection from range"));
1035 selection->set (0, loc.start(), loc.end());
1036 commit_reversible_command ();
1038 if (!Profile->get_sae()) {
1039 set_mouse_mode (Editing::MouseRange, false);
1043 void
1044 Editor::select_all_selectables_using_time_selection ()
1046 list<Selectable *> touched;
1048 if (selection->time.empty()) {
1049 return;
1052 nframes64_t start = selection->time[clicked_selection].start;
1053 nframes64_t end = selection->time[clicked_selection].end;
1055 if (end - start < 1) {
1056 return;
1059 TrackSelection* ts;
1061 if (selection->tracks.empty()) {
1062 ts = &track_views;
1063 } else {
1064 ts = &selection->tracks;
1067 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1068 if ((*iter)->hidden()) {
1069 continue;
1071 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1074 begin_reversible_command (_("select all from range"));
1075 selection->set (touched);
1076 commit_reversible_command ();
1080 void
1081 Editor::select_all_selectables_using_punch()
1083 Location* location = session->locations()->auto_punch_location();
1084 list<Selectable *> touched;
1086 if (location == 0 || (location->end() - location->start() <= 1)) {
1087 return;
1091 TrackSelection* ts;
1093 if (selection->tracks.empty()) {
1094 ts = &track_views;
1095 } else {
1096 ts = &selection->tracks;
1099 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1100 if ((*iter)->hidden()) {
1101 continue;
1103 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1105 begin_reversible_command (_("select all from punch"));
1106 selection->set (touched);
1107 commit_reversible_command ();
1111 void
1112 Editor::select_all_selectables_using_loop()
1114 Location* location = session->locations()->auto_loop_location();
1115 list<Selectable *> touched;
1117 if (location == 0 || (location->end() - location->start() <= 1)) {
1118 return;
1122 TrackSelection* ts;
1124 if (selection->tracks.empty()) {
1125 ts = &track_views;
1126 } else {
1127 ts = &selection->tracks;
1130 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1131 if ((*iter)->hidden()) {
1132 continue;
1134 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1136 begin_reversible_command (_("select all from loop"));
1137 selection->set (touched);
1138 commit_reversible_command ();
1142 void
1143 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1145 nframes64_t start;
1146 nframes64_t end;
1147 list<Selectable *> touched;
1149 if (after) {
1150 begin_reversible_command (_("select all after cursor"));
1151 start = cursor->current_frame ;
1152 end = session->current_end_frame();
1153 } else {
1154 if (cursor->current_frame > 0) {
1155 begin_reversible_command (_("select all before cursor"));
1156 start = 0;
1157 end = cursor->current_frame - 1;
1158 } else {
1159 return;
1164 TrackSelection* ts;
1166 if (selection->tracks.empty()) {
1167 ts = &track_views;
1168 } else {
1169 ts = &selection->tracks;
1172 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1173 if ((*iter)->hidden()) {
1174 continue;
1176 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1178 selection->set (touched);
1179 commit_reversible_command ();
1182 void
1183 Editor::select_all_selectables_using_edit (bool after)
1185 nframes64_t start;
1186 nframes64_t end;
1187 list<Selectable *> touched;
1189 if (after) {
1190 begin_reversible_command (_("select all after edit"));
1191 start = get_preferred_edit_position();
1192 end = session->current_end_frame();
1193 } else {
1194 if ((end = get_preferred_edit_position()) > 1) {
1195 begin_reversible_command (_("select all before edit"));
1196 start = 0;
1197 end -= 1;
1198 } else {
1199 return;
1204 TrackSelection* ts;
1206 if (selection->tracks.empty()) {
1207 ts = &track_views;
1208 } else {
1209 ts = &selection->tracks;
1212 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1213 if ((*iter)->hidden()) {
1214 continue;
1216 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1218 selection->set (touched);
1219 commit_reversible_command ();
1222 void
1223 Editor::select_all_selectables_between (bool within)
1225 nframes64_t start;
1226 nframes64_t end;
1227 list<Selectable *> touched;
1229 if (!get_edit_op_range (start, end)) {
1230 return;
1233 TrackSelection* ts;
1235 if (selection->tracks.empty()) {
1236 ts = &track_views;
1237 } else {
1238 ts = &selection->tracks;
1241 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1242 if ((*iter)->hidden()) {
1243 continue;
1245 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1248 selection->set (touched);
1251 void
1252 Editor::select_range_between ()
1254 nframes64_t start;
1255 nframes64_t end;
1257 if (!get_edit_op_range (start, end)) {
1258 return;
1261 set_mouse_mode (MouseRange);
1262 selection->set ((TimeAxisView*) 0, start, end);
1265 bool
1266 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1268 nframes64_t m;
1269 bool ignored;
1271 /* in range mode, use any existing selection */
1273 if (mouse_mode == MouseRange && !selection->time.empty()) {
1274 /* we know that these are ordered */
1275 start = selection->time.start();
1276 end = selection->time.end_frame();
1277 return true;
1280 if (!mouse_frame (m, ignored)) {
1281 /* mouse is not in a canvas, try playhead+selected marker.
1282 this is probably most true when using menus.
1285 if (selection->markers.empty()) {
1286 return false;
1289 start = selection->markers.front()->position();
1290 end = session->audible_frame();
1292 } else {
1294 switch (_edit_point) {
1295 case EditAtPlayhead:
1296 if (selection->markers.empty()) {
1297 /* use mouse + playhead */
1298 start = m;
1299 end = session->audible_frame();
1300 } else {
1301 /* use playhead + selected marker */
1302 start = session->audible_frame();
1303 end = selection->markers.front()->position();
1305 break;
1307 case EditAtMouse:
1308 /* use mouse + selected marker */
1309 if (selection->markers.empty()) {
1310 start = m;
1311 end = session->audible_frame();
1312 } else {
1313 start = selection->markers.front()->position();
1314 end = m;
1316 break;
1318 case EditAtSelectedMarker:
1319 /* use mouse + selected marker */
1320 if (selection->markers.empty()) {
1322 MessageDialog win (_("No edit range defined"),
1323 false,
1324 MESSAGE_INFO,
1325 BUTTONS_OK);
1327 win.set_secondary_text (
1328 _("the edit point is Selected Marker\nbut there is no selected marker."));
1331 win.set_default_response (RESPONSE_CLOSE);
1332 win.set_position (Gtk::WIN_POS_MOUSE);
1333 win.show_all();
1335 win.run ();
1337 return false; // NO RANGE
1339 start = selection->markers.front()->position();
1340 end = m;
1341 break;
1345 if (start == end) {
1346 return false;
1349 if (start > end) {
1350 swap (start, end);
1353 /* turn range into one delimited by start...end,
1354 not start...end-1
1357 end++;
1359 return true;
1362 void
1363 Editor::deselect_all ()
1365 selection->clear ();