force AIFF format exported files to big-endian
[ardour2.git] / gtk2_ardour / editor_selection.cc
blobb82d2964292c5bf98de910c3c7788f9434f18aca
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());
837 void
838 Editor::point_selection_changed ()
840 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
841 (*i)->set_selected_points (selection->points);
845 void
846 Editor::select_all_in_track (Selection::Operation op)
848 list<Selectable *> touched;
850 if (!clicked_trackview) {
851 return;
854 clicked_trackview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
856 switch (op) {
857 case Selection::Toggle:
858 selection->add (touched);
859 break;
860 case Selection::Set:
861 selection->set (touched);
862 break;
863 case Selection::Extend:
864 /* meaningless, because we're selecting everything */
865 break;
866 case Selection::Add:
867 selection->add (touched);
868 break;
872 void
873 Editor::select_all (Selection::Operation op)
875 list<Selectable *> touched;
877 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
878 if ((*iter)->hidden()) {
879 continue;
881 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
883 begin_reversible_command (_("select all"));
884 switch (op) {
885 case Selection::Add:
886 selection->add (touched);
887 break;
888 case Selection::Toggle:
889 selection->add (touched);
890 break;
891 case Selection::Set:
892 selection->set (touched);
893 break;
894 case Selection::Extend:
895 /* meaningless, because we're selecting everything */
896 break;
898 commit_reversible_command ();
901 void
902 Editor::invert_selection_in_track ()
904 list<Selectable *> touched;
906 if (!clicked_trackview) {
907 return;
910 clicked_trackview->get_inverted_selectables (*selection, touched);
911 selection->set (touched);
914 void
915 Editor::invert_selection ()
917 list<Selectable *> touched;
919 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
920 if ((*iter)->hidden()) {
921 continue;
923 (*iter)->get_inverted_selectables (*selection, touched);
926 selection->set (touched);
929 bool
930 Editor::select_all_within (nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
932 list<Selectable*> touched;
933 list<Selectable*>::size_type n = 0;
934 TrackViewList touched_tracks;
936 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
937 if ((*iter)->hidden()) {
938 continue;
941 n = touched.size();
943 (*iter)->get_selectables (start, end, top, bot, touched);
945 if (n != touched.size()) {
946 touched_tracks.push_back (*iter);
950 if (touched.empty()) {
951 return false;
954 if (!touched_tracks.empty()) {
956 switch (op) {
957 case Selection::Add:
958 selection->add (touched_tracks);
959 break;
960 case Selection::Toggle:
961 selection->toggle (touched_tracks);
962 break;
963 case Selection::Set:
964 selection->set (touched_tracks);
965 break;
966 case Selection::Extend:
967 /* not defined yet */
968 break;
972 begin_reversible_command (_("select all within"));
973 switch (op) {
974 case Selection::Add:
975 selection->add (touched);
976 break;
977 case Selection::Toggle:
978 selection->toggle (touched);
979 break;
980 case Selection::Set:
981 selection->set (touched);
982 break;
983 case Selection::Extend:
984 /* not defined yet */
985 break;
988 commit_reversible_command ();
990 return !touched.empty();
993 void
994 Editor::set_selection_from_audio_region ()
996 if (selection->regions.empty()) {
997 return;
1000 selection->set (0, selection->regions.start(), selection->regions.end_frame());
1001 if (!Profile->get_sae()) {
1002 set_mouse_mode (Editing::MouseRange, false);
1006 void
1007 Editor::set_selection_from_punch()
1009 Location* location;
1011 if ((location = session->locations()->auto_punch_location()) == 0) {
1012 return;
1015 set_selection_from_range (*location);
1018 void
1019 Editor::set_selection_from_loop()
1021 Location* location;
1023 if ((location = session->locations()->auto_loop_location()) == 0) {
1024 return;
1026 set_selection_from_range (*location);
1029 void
1030 Editor::set_selection_from_range (Location& loc)
1032 begin_reversible_command (_("set selection from range"));
1033 selection->set (0, loc.start(), loc.end());
1034 commit_reversible_command ();
1036 if (!Profile->get_sae()) {
1037 set_mouse_mode (Editing::MouseRange, false);
1041 void
1042 Editor::select_all_selectables_using_time_selection ()
1044 list<Selectable *> touched;
1046 if (selection->time.empty()) {
1047 return;
1050 nframes64_t start = selection->time[clicked_selection].start;
1051 nframes64_t end = selection->time[clicked_selection].end;
1053 if (end - start < 1) {
1054 return;
1057 TrackSelection* ts;
1059 if (selection->tracks.empty()) {
1060 ts = &track_views;
1061 } else {
1062 ts = &selection->tracks;
1065 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1066 if ((*iter)->hidden()) {
1067 continue;
1069 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1072 begin_reversible_command (_("select all from range"));
1073 selection->set (touched);
1074 commit_reversible_command ();
1078 void
1079 Editor::select_all_selectables_using_punch()
1081 Location* location = session->locations()->auto_punch_location();
1082 list<Selectable *> touched;
1084 if (location == 0 || (location->end() - location->start() <= 1)) {
1085 return;
1089 TrackSelection* ts;
1091 if (selection->tracks.empty()) {
1092 ts = &track_views;
1093 } else {
1094 ts = &selection->tracks;
1097 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1098 if ((*iter)->hidden()) {
1099 continue;
1101 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1103 begin_reversible_command (_("select all from punch"));
1104 selection->set (touched);
1105 commit_reversible_command ();
1109 void
1110 Editor::select_all_selectables_using_loop()
1112 Location* location = session->locations()->auto_loop_location();
1113 list<Selectable *> touched;
1115 if (location == 0 || (location->end() - location->start() <= 1)) {
1116 return;
1120 TrackSelection* ts;
1122 if (selection->tracks.empty()) {
1123 ts = &track_views;
1124 } else {
1125 ts = &selection->tracks;
1128 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1129 if ((*iter)->hidden()) {
1130 continue;
1132 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1134 begin_reversible_command (_("select all from loop"));
1135 selection->set (touched);
1136 commit_reversible_command ();
1140 void
1141 Editor::select_all_selectables_using_cursor (Cursor *cursor, bool after)
1143 nframes64_t start;
1144 nframes64_t end;
1145 list<Selectable *> touched;
1147 if (after) {
1148 begin_reversible_command (_("select all after cursor"));
1149 start = cursor->current_frame ;
1150 end = session->current_end_frame();
1151 } else {
1152 if (cursor->current_frame > 0) {
1153 begin_reversible_command (_("select all before cursor"));
1154 start = 0;
1155 end = cursor->current_frame - 1;
1156 } else {
1157 return;
1162 TrackSelection* ts;
1164 if (selection->tracks.empty()) {
1165 ts = &track_views;
1166 } else {
1167 ts = &selection->tracks;
1170 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1171 if ((*iter)->hidden()) {
1172 continue;
1174 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1176 selection->set (touched);
1177 commit_reversible_command ();
1180 void
1181 Editor::select_all_selectables_using_edit (bool after)
1183 nframes64_t start;
1184 nframes64_t end;
1185 list<Selectable *> touched;
1187 if (after) {
1188 begin_reversible_command (_("select all after edit"));
1189 start = get_preferred_edit_position();
1190 end = session->current_end_frame();
1191 } else {
1192 if ((end = get_preferred_edit_position()) > 1) {
1193 begin_reversible_command (_("select all before edit"));
1194 start = 0;
1195 end -= 1;
1196 } else {
1197 return;
1202 TrackSelection* ts;
1204 if (selection->tracks.empty()) {
1205 ts = &track_views;
1206 } else {
1207 ts = &selection->tracks;
1210 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1211 if ((*iter)->hidden()) {
1212 continue;
1214 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1216 selection->set (touched);
1217 commit_reversible_command ();
1220 void
1221 Editor::select_all_selectables_between (bool within)
1223 nframes64_t start;
1224 nframes64_t end;
1225 list<Selectable *> touched;
1227 if (!get_edit_op_range (start, end)) {
1228 return;
1231 TrackSelection* ts;
1233 if (selection->tracks.empty()) {
1234 ts = &track_views;
1235 } else {
1236 ts = &selection->tracks;
1239 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1240 if ((*iter)->hidden()) {
1241 continue;
1243 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1246 selection->set (touched);
1249 void
1250 Editor::select_range_between ()
1252 nframes64_t start;
1253 nframes64_t end;
1255 if (!get_edit_op_range (start, end)) {
1256 return;
1259 set_mouse_mode (MouseRange);
1260 selection->set ((TimeAxisView*) 0, start, end);
1263 bool
1264 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1266 nframes64_t m;
1267 bool ignored;
1269 /* in range mode, use any existing selection */
1271 if (mouse_mode == MouseRange && !selection->time.empty()) {
1272 /* we know that these are ordered */
1273 start = selection->time.start();
1274 end = selection->time.end_frame();
1275 return true;
1278 if (!mouse_frame (m, ignored)) {
1279 /* mouse is not in a canvas, try playhead+selected marker.
1280 this is probably most true when using menus.
1283 if (selection->markers.empty()) {
1284 return false;
1287 start = selection->markers.front()->position();
1288 end = session->audible_frame();
1290 } else {
1292 switch (_edit_point) {
1293 case EditAtPlayhead:
1294 if (selection->markers.empty()) {
1295 /* use mouse + playhead */
1296 start = m;
1297 end = session->audible_frame();
1298 } else {
1299 /* use playhead + selected marker */
1300 start = session->audible_frame();
1301 end = selection->markers.front()->position();
1303 break;
1305 case EditAtMouse:
1306 /* use mouse + selected marker */
1307 if (selection->markers.empty()) {
1308 start = m;
1309 end = session->audible_frame();
1310 } else {
1311 start = selection->markers.front()->position();
1312 end = m;
1314 break;
1316 case EditAtSelectedMarker:
1317 /* use mouse + selected marker */
1318 if (selection->markers.empty()) {
1320 MessageDialog win (_("No edit range defined"),
1321 false,
1322 MESSAGE_INFO,
1323 BUTTONS_OK);
1325 win.set_secondary_text (
1326 _("the edit point is Selected Marker\nbut there is no selected marker."));
1329 win.set_default_response (RESPONSE_CLOSE);
1330 win.set_position (Gtk::WIN_POS_MOUSE);
1331 win.show_all();
1333 win.run ();
1335 return false; // NO RANGE
1337 start = selection->markers.front()->position();
1338 end = m;
1339 break;
1343 if (start == end) {
1344 return false;
1347 if (start > end) {
1348 swap (start, end);
1351 /* turn range into one delimited by start...end,
1352 not start...end-1
1355 end++;
1357 return true;
1360 void
1361 Editor::deselect_all ()
1363 selection->clear ();