fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / editor_selection.cc
blobfda5155e03d1fe6ed69505ab4f2bf8c253b8383a
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/session.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"
36 #include "control_point.h"
37 #include "editor_regions.h"
39 #include "i18n.h"
41 using namespace std;
42 using namespace ARDOUR;
43 using namespace PBD;
44 using namespace Gtk;
45 using namespace Glib;
46 using namespace Gtkmm2ext;
47 using namespace Editing;
49 struct TrackViewByPositionSorter
51 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
52 return a->y_position() < b->y_position();
56 bool
57 Editor::extend_selection_to_track (TimeAxisView& view)
59 if (selection->selected (&view)) {
60 /* already selected, do nothing */
61 return false;
64 if (selection->tracks.empty()) {
66 if (!selection->selected (&view)) {
67 selection->set (&view);
68 return true;
69 } else {
70 return false;
74 /* something is already selected, so figure out which range of things to add */
76 TrackViewList to_be_added;
77 TrackViewList sorted = track_views;
78 TrackViewByPositionSorter cmp;
79 bool passed_clicked = false;
80 bool forwards = true;
82 sorted.sort (cmp);
84 if (!selection->selected (&view)) {
85 to_be_added.push_back (&view);
88 /* figure out if we should go forward or backwards */
90 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
92 if ((*i) == &view) {
93 passed_clicked = true;
96 if (selection->selected (*i)) {
97 if (passed_clicked) {
98 forwards = true;
99 } else {
100 forwards = false;
102 break;
106 passed_clicked = false;
108 if (forwards) {
110 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
112 if ((*i) == &view) {
113 passed_clicked = true;
114 continue;
117 if (passed_clicked) {
118 if ((*i)->hidden()) {
119 continue;
121 if (selection->selected (*i)) {
122 break;
123 } else if (!(*i)->hidden()) {
124 to_be_added.push_back (*i);
129 } else {
131 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
133 if ((*r) == &view) {
134 passed_clicked = true;
135 continue;
138 if (passed_clicked) {
140 if ((*r)->hidden()) {
141 continue;
144 if (selection->selected (*r)) {
145 break;
146 } else if (!(*r)->hidden()) {
147 to_be_added.push_back (*r);
153 if (!to_be_added.empty()) {
154 selection->add (to_be_added);
155 return true;
158 return false;
161 void
162 Editor::select_all_tracks ()
164 TrackViewList visible_views;
165 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
166 if ((*i)->marked_for_display()) {
167 visible_views.push_back (*i);
170 selection->set (visible_views);
173 /** Select clicked_routeview, unless there are no currently selected
174 * tracks, in which case nothing will happen unless `force' is true.
176 void
177 Editor::set_selected_track_as_side_effect (bool force)
179 if (!clicked_routeview) {
180 return;
183 if (!selection->tracks.empty()) {
184 if (!selection->selected (clicked_routeview)) {
185 selection->add (clicked_routeview);
188 } else if (force) {
189 selection->set (clicked_routeview);
193 void
194 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
196 switch (op) {
197 case Selection::Toggle:
198 if (selection->selected (&view)) {
199 if (!no_remove) {
200 selection->remove (&view);
202 } else {
203 selection->add (&view);
205 break;
207 case Selection::Add:
208 if (!selection->selected (&view)) {
209 selection->add (&view);
211 break;
213 case Selection::Set:
214 selection->set (&view);
215 break;
217 case Selection::Extend:
218 extend_selection_to_track (view);
219 break;
223 void
224 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
226 if (!clicked_routeview) {
227 return;
230 if (!press) {
231 return;
234 set_selected_track (*clicked_routeview, op, no_remove);
237 bool
238 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
240 if (!clicked_control_point) {
241 return false;
244 if (clicked_control_point->selected()) {
245 /* the clicked control point is already selected; others may be as well, so
246 don't change the selection.
248 return true;
251 /* We know the ControlPoint that was clicked, but (as discussed in automation_selectable.h)
252 * selected automation data are described by areas on the AutomationLine. A ControlPoint
253 * represents any model points in the space that it takes up, so the AutomationSelectable
254 * needs to be the size of the ControlPoint.
257 double const size = clicked_control_point->size ();
259 nframes64_t const x1 = pixel_to_frame (clicked_control_point->get_x() - size / 2);
260 nframes64_t const x2 = pixel_to_frame (clicked_control_point->get_x() + size / 2);
261 double y1 = clicked_control_point->get_y() - size / 2;
262 double y2 = clicked_control_point->get_y() + size / 2;
264 /* convert the y values to trackview space */
265 double dummy = 0;
266 clicked_control_point->line().parent_group().i2w (dummy, y1);
267 clicked_control_point->line().parent_group().i2w (dummy, y2);
268 _trackview_group->w2i (dummy, y1);
269 _trackview_group->w2i (dummy, y2);
271 /* and set up the selection */
272 return select_all_within (x1, x2, y1, y2, selection->tracks, Selection::Set);
275 void
276 Editor::get_onscreen_tracks (TrackViewList& tvl)
278 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
279 if ((*i)->y_position() < _canvas_height) {
280 tvl.push_back (*i);
285 /** Call a slot for a given `basis' track and also for any track that is in the same
286 * active route group with a particular set of properties.
288 * @param sl Slot to call.
289 * @param basis Basis track.
290 * @param prop Properties that active edit groups must share to be included in the map.
293 void
294 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
296 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
297 if (route_basis == 0) {
298 return;
301 set<RouteTimeAxisView*> tracks;
302 tracks.insert (route_basis);
304 RouteGroup* group = route_basis->route()->route_group();
305 if (group && group->enabled_property (prop)) {
307 /* the basis is a member of an active route group, with the appropriate
308 properties; find other members */
310 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
311 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
312 if (v && v->route()->route_group() == group) {
313 tracks.insert (v);
318 /* call the slots */
319 uint32_t const sz = tracks.size ();
320 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
321 sl (**i, sz);
325 void
326 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
328 boost::shared_ptr<Playlist> pl;
329 vector<boost::shared_ptr<Region> > results;
330 RegionView* marv;
331 boost::shared_ptr<Track> tr;
333 if ((tr = tv.track()) == 0) {
334 /* bus */
335 return;
338 if (&tv == &basis->get_time_axis_view()) {
339 /* looking in same track as the original */
340 return;
343 if ((pl = tr->playlist()) != 0) {
344 pl->get_equivalent_regions (basis->region(), results);
347 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
348 if ((marv = tv.view()->find_view (*ir)) != 0) {
349 all_equivs->push_back (marv);
354 void
355 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
357 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview(), property);
359 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
361 equivalent_regions.push_back (basis);
364 RegionSelection
365 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
367 RegionSelection equivalent;
369 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
371 vector<RegionView*> eq;
373 mapover_tracks (
374 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
375 &(*i)->get_trackview(), prop
378 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
379 equivalent.add (*j);
382 equivalent.add (*i);
385 return equivalent;
390 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
392 int region_count = 0;
394 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
396 RouteTimeAxisView* tatv;
398 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
400 boost::shared_ptr<Playlist> pl;
401 vector<boost::shared_ptr<Region> > results;
402 RegionView* marv;
403 boost::shared_ptr<Track> tr;
405 if ((tr = tatv->track()) == 0) {
406 /* bus */
407 continue;
410 if ((pl = (tr->playlist())) != 0) {
411 pl->get_region_list_equivalent_regions (region, results);
414 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
415 if ((marv = tatv->view()->find_view (*ir)) != 0) {
416 region_count++;
423 return region_count;
427 bool
428 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
430 vector<RegionView*> all_equivalent_regions;
431 bool commit = false;
433 if (!clicked_regionview || !clicked_routeview) {
434 return false;
437 if (press) {
438 button_release_can_deselect = false;
441 if (op == Selection::Toggle || op == Selection::Set) {
444 switch (op) {
445 case Selection::Toggle:
447 if (selection->selected (clicked_regionview)) {
448 if (press) {
450 /* whatever was clicked was selected already; do nothing here but allow
451 the button release to deselect it
454 button_release_can_deselect = true;
456 } else {
458 if (button_release_can_deselect) {
460 /* just remove this one region, but only on a permitted button release */
462 selection->remove (clicked_regionview);
463 commit = true;
465 /* no more deselect action on button release till a new press
466 finds an already selected object.
469 button_release_can_deselect = false;
473 } else {
475 if (press) {
477 if (selection->selected (clicked_routeview)) {
478 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
479 } else {
480 all_equivalent_regions.push_back (clicked_regionview);
483 /* add all the equivalent regions, but only on button press */
485 if (!all_equivalent_regions.empty()) {
486 commit = true;
489 selection->add (all_equivalent_regions);
492 break;
494 case Selection::Set:
495 if (!selection->selected (clicked_regionview)) {
496 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
497 selection->set (all_equivalent_regions);
498 commit = true;
499 } else {
500 /* no commit necessary: clicked on an already selected region */
501 goto out;
503 break;
505 default:
506 /* silly compiler */
507 break;
510 } else if (op == Selection::Extend) {
512 list<Selectable*> results;
513 nframes64_t last_frame;
514 nframes64_t first_frame;
515 bool same_track = false;
517 /* 1. find the last selected regionview in the track that was clicked in */
519 last_frame = 0;
520 first_frame = max_frames;
522 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
523 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
525 if ((*x)->region()->last_frame() > last_frame) {
526 last_frame = (*x)->region()->last_frame();
529 if ((*x)->region()->first_frame() < first_frame) {
530 first_frame = (*x)->region()->first_frame();
533 same_track = true;
537 if (same_track) {
539 /* 2. figure out the boundaries for our search for new objects */
541 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
542 case OverlapNone:
543 if (last_frame < clicked_regionview->region()->first_frame()) {
544 first_frame = last_frame;
545 last_frame = clicked_regionview->region()->last_frame();
546 } else {
547 last_frame = first_frame;
548 first_frame = clicked_regionview->region()->first_frame();
550 break;
552 case OverlapExternal:
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();
560 break;
562 case OverlapInternal:
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();
570 break;
572 case OverlapStart:
573 case OverlapEnd:
574 /* nothing to do except add clicked region to selection, since it
575 overlaps with the existing selection in this track.
577 break;
580 } else {
582 /* click in a track that has no regions selected, so extend vertically
583 to pick out all regions that are defined by the existing selection
584 plus this one.
588 first_frame = entered_regionview->region()->position();
589 last_frame = entered_regionview->region()->last_frame();
591 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
592 if ((*i)->region()->position() < first_frame) {
593 first_frame = (*i)->region()->position();
595 if ((*i)->region()->last_frame() + 1 > last_frame) {
596 last_frame = (*i)->region()->last_frame();
601 /* 2. find all the tracks we should select in */
603 set<RouteTimeAxisView*> relevant_tracks;
605 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
606 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
607 if (r) {
608 relevant_tracks.insert (r);
612 set<RouteTimeAxisView*> already_in_selection;
614 if (relevant_tracks.empty()) {
616 /* no tracks selected .. thus .. if the
617 regionview we're in isn't selected
618 (i.e. we're about to extend to it), then
619 find all tracks between the this one and
620 any selected ones.
623 if (!selection->selected (entered_regionview)) {
625 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
627 if (rtv) {
629 /* add this track to the ones we will search */
631 relevant_tracks.insert (rtv);
633 /* find the track closest to this one that
634 already a selected region.
637 RouteTimeAxisView* closest = 0;
638 int distance = INT_MAX;
639 int key = rtv->route()->order_key ("editor");
641 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
643 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
645 if (artv && artv != rtv) {
647 pair<set<RouteTimeAxisView*>::iterator,bool> result;
649 result = already_in_selection.insert (artv);
651 if (result.second) {
652 /* newly added to already_in_selection */
654 int d = artv->route()->order_key ("editor");
656 d -= key;
658 if (abs (d) < distance) {
659 distance = abs (d);
660 closest = artv;
666 if (closest) {
668 /* now add all tracks between that one and this one */
670 int okey = closest->route()->order_key ("editor");
672 if (okey > key) {
673 swap (okey, key);
676 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
677 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
678 if (artv && artv != rtv) {
680 int k = artv->route()->order_key ("editor");
682 if (k >= okey && k <= key) {
684 /* in range but don't add it if
685 it already has tracks selected.
686 this avoids odd selection
687 behaviour that feels wrong.
690 if (find (already_in_selection.begin(),
691 already_in_selection.end(),
692 artv) == already_in_selection.end()) {
694 relevant_tracks.insert (artv);
704 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
705 one that was clicked.
708 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
709 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
712 /* 4. convert to a vector of regions */
714 vector<RegionView*> regions;
716 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
717 RegionView* arv;
719 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
720 regions.push_back (arv);
724 if (!regions.empty()) {
725 selection->add (regions);
726 commit = true;
730 out:
731 return commit;
735 void
736 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
738 vector<RegionView*> all_equivalent_regions;
740 get_regions_corresponding_to (region, all_equivalent_regions);
742 if (all_equivalent_regions.empty()) {
743 return;
746 begin_reversible_command (_("set selected regions"));
748 switch (op) {
749 case Selection::Toggle:
750 /* XXX this is not correct */
751 selection->toggle (all_equivalent_regions);
752 break;
753 case Selection::Set:
754 selection->set (all_equivalent_regions);
755 break;
756 case Selection::Extend:
757 selection->add (all_equivalent_regions);
758 break;
759 case Selection::Add:
760 selection->add (all_equivalent_regions);
761 break;
764 commit_reversible_command () ;
767 bool
768 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
770 RegionView* rv;
771 boost::shared_ptr<Region> r (weak_r.lock());
773 if (!r) {
774 return true;
777 if ((rv = sv->find_view (r)) == 0) {
778 return true;
781 /* don't reset the selection if its something other than
782 a single other region.
785 if (selection->regions.size() > 1) {
786 return true;
789 begin_reversible_command (_("set selected regions"));
791 selection->set (rv);
793 commit_reversible_command () ;
795 return true;
798 void
799 Editor::track_selection_changed ()
801 switch (selection->tracks.size()){
802 case 0:
803 break;
804 default:
805 set_selected_mixer_strip (*(selection->tracks.front()));
806 break;
809 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
810 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
811 (*i)->set_selected (true);
812 } else {
813 (*i)->set_selected (false);
817 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
820 void
821 Editor::time_selection_changed ()
823 if (Profile->get_sae()) {
824 return;
827 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
828 (*i)->hide_selection ();
831 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
832 (*i)->show_selection (selection->time);
835 if (selection->time.empty()) {
836 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
837 } else {
838 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
842 void
843 Editor::sensitize_the_right_region_actions (bool have_selected_regions)
845 for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
846 x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
848 string accel_path = (*x)->get_accel_path ();
849 AccelKey key;
851 /* if there is an accelerator, it should always be sensitive
852 to allow for keyboard ops on entered regions.
855 bool known = ActionManager::lookup_entry (accel_path, key);
857 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
858 (*x)->set_sensitive (true);
859 } else {
860 (*x)->set_sensitive (have_selected_regions);
866 void
867 Editor::region_selection_changed ()
869 _regions->block_change_connection (true);
870 editor_regions_selection_changed_connection.block(true);
872 _regions->unselect_all ();
874 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
875 (*i)->set_selected_regionviews (selection->regions);
878 _regions->set_selected (selection->regions);
880 sensitize_the_right_region_actions (!selection->regions.empty());
882 _regions->block_change_connection (false);
883 editor_regions_selection_changed_connection.block(false);
886 void
887 Editor::point_selection_changed ()
889 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
890 (*i)->set_selected_points (selection->points);
894 void
895 Editor::select_all_in_track (Selection::Operation op)
897 list<Selectable *> touched;
899 if (!clicked_routeview) {
900 return;
903 clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
905 switch (op) {
906 case Selection::Toggle:
907 selection->add (touched);
908 break;
909 case Selection::Set:
910 selection->set (touched);
911 break;
912 case Selection::Extend:
913 /* meaningless, because we're selecting everything */
914 break;
915 case Selection::Add:
916 selection->add (touched);
917 break;
921 void
922 Editor::select_all (Selection::Operation op)
924 list<Selectable *> touched;
926 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
927 if ((*iter)->hidden()) {
928 continue;
930 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
932 begin_reversible_command (_("select all"));
933 switch (op) {
934 case Selection::Add:
935 selection->add (touched);
936 break;
937 case Selection::Toggle:
938 selection->add (touched);
939 break;
940 case Selection::Set:
941 selection->set (touched);
942 break;
943 case Selection::Extend:
944 /* meaningless, because we're selecting everything */
945 break;
947 commit_reversible_command ();
949 void
950 Editor::invert_selection_in_track ()
952 list<Selectable *> touched;
954 if (!clicked_routeview) {
955 return;
958 clicked_routeview->get_inverted_selectables (*selection, touched);
959 selection->set (touched);
962 void
963 Editor::invert_selection ()
965 list<Selectable *> touched;
967 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
968 if ((*iter)->hidden()) {
969 continue;
971 (*iter)->get_inverted_selectables (*selection, touched);
974 selection->set (touched);
977 /** @param top Top (lower) y limit in trackview coordinates.
978 * @param bottom Bottom (higher) y limit in trackview coordinates.
980 bool
981 Editor::select_all_within (nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
983 list<Selectable*> found;
985 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
987 if ((*iter)->hidden()) {
988 continue;
991 (*iter)->get_selectables (start, end, top, bot, found);
994 if (found.empty()) {
995 return false;
998 begin_reversible_command (_("select all within"));
999 switch (op) {
1000 case Selection::Add:
1001 selection->add (found);
1002 break;
1003 case Selection::Toggle:
1004 selection->toggle (found);
1005 break;
1006 case Selection::Set:
1007 selection->set (found);
1008 break;
1009 case Selection::Extend:
1010 /* not defined yet */
1011 break;
1014 commit_reversible_command ();
1016 return !found.empty();
1019 void
1020 Editor::set_selection_from_region ()
1022 if (selection->regions.empty()) {
1023 return;
1026 selection->set (selection->regions.start(), selection->regions.end_frame());
1027 if (!Profile->get_sae()) {
1028 set_mouse_mode (Editing::MouseRange, false);
1032 void
1033 Editor::set_selection_from_punch()
1035 Location* location;
1037 if ((location = _session->locations()->auto_punch_location()) == 0) {
1038 return;
1041 set_selection_from_range (*location);
1044 void
1045 Editor::set_selection_from_loop()
1047 Location* location;
1049 if ((location = _session->locations()->auto_loop_location()) == 0) {
1050 return;
1052 set_selection_from_range (*location);
1055 void
1056 Editor::set_selection_from_range (Location& loc)
1058 begin_reversible_command (_("set selection from range"));
1059 selection->set (loc.start(), loc.end());
1060 commit_reversible_command ();
1062 if (!Profile->get_sae()) {
1063 set_mouse_mode (Editing::MouseRange, false);
1067 void
1068 Editor::select_all_selectables_using_time_selection ()
1070 list<Selectable *> touched;
1072 if (selection->time.empty()) {
1073 return;
1076 nframes64_t start = selection->time[clicked_selection].start;
1077 nframes64_t end = selection->time[clicked_selection].end;
1079 if (end - start < 1) {
1080 return;
1083 TrackViewList* ts;
1085 if (selection->tracks.empty()) {
1086 ts = &track_views;
1087 } else {
1088 ts = &selection->tracks;
1091 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1092 if ((*iter)->hidden()) {
1093 continue;
1095 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1098 begin_reversible_command (_("select all from range"));
1099 selection->set (touched);
1100 commit_reversible_command ();
1104 void
1105 Editor::select_all_selectables_using_punch()
1107 Location* location = _session->locations()->auto_punch_location();
1108 list<Selectable *> touched;
1110 if (location == 0 || (location->end() - location->start() <= 1)) {
1111 return;
1115 TrackViewList* ts;
1117 if (selection->tracks.empty()) {
1118 ts = &track_views;
1119 } else {
1120 ts = &selection->tracks;
1123 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1124 if ((*iter)->hidden()) {
1125 continue;
1127 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1129 begin_reversible_command (_("select all from punch"));
1130 selection->set (touched);
1131 commit_reversible_command ();
1135 void
1136 Editor::select_all_selectables_using_loop()
1138 Location* location = _session->locations()->auto_loop_location();
1139 list<Selectable *> touched;
1141 if (location == 0 || (location->end() - location->start() <= 1)) {
1142 return;
1146 TrackViewList* ts;
1148 if (selection->tracks.empty()) {
1149 ts = &track_views;
1150 } else {
1151 ts = &selection->tracks;
1154 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1155 if ((*iter)->hidden()) {
1156 continue;
1158 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1160 begin_reversible_command (_("select all from loop"));
1161 selection->set (touched);
1162 commit_reversible_command ();
1166 void
1167 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1169 nframes64_t start;
1170 nframes64_t end;
1171 list<Selectable *> touched;
1173 if (after) {
1174 begin_reversible_command (_("select all after cursor"));
1175 start = cursor->current_frame ;
1176 end = _session->current_end_frame();
1177 } else {
1178 if (cursor->current_frame > 0) {
1179 begin_reversible_command (_("select all before cursor"));
1180 start = 0;
1181 end = cursor->current_frame - 1;
1182 } else {
1183 return;
1188 TrackViewList* ts;
1190 if (selection->tracks.empty()) {
1191 ts = &track_views;
1192 } else {
1193 ts = &selection->tracks;
1196 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1197 if ((*iter)->hidden()) {
1198 continue;
1200 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1202 selection->set (touched);
1203 commit_reversible_command ();
1206 void
1207 Editor::select_all_selectables_using_edit (bool after)
1209 nframes64_t start;
1210 nframes64_t end;
1211 list<Selectable *> touched;
1213 if (after) {
1214 begin_reversible_command (_("select all after edit"));
1215 start = get_preferred_edit_position();
1216 end = _session->current_end_frame();
1217 } else {
1218 if ((end = get_preferred_edit_position()) > 1) {
1219 begin_reversible_command (_("select all before edit"));
1220 start = 0;
1221 end -= 1;
1222 } else {
1223 return;
1228 TrackViewList* ts;
1230 if (selection->tracks.empty()) {
1231 ts = &track_views;
1232 } else {
1233 ts = &selection->tracks;
1236 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1237 if ((*iter)->hidden()) {
1238 continue;
1240 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1242 selection->set (touched);
1243 commit_reversible_command ();
1246 void
1247 Editor::select_all_selectables_between (bool /*within*/)
1249 nframes64_t start;
1250 nframes64_t end;
1251 list<Selectable *> touched;
1253 if (!get_edit_op_range (start, end)) {
1254 return;
1257 TrackViewList* ts;
1259 if (selection->tracks.empty()) {
1260 ts = &track_views;
1261 } else {
1262 ts = &selection->tracks;
1265 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1266 if ((*iter)->hidden()) {
1267 continue;
1269 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1272 selection->set (touched);
1275 void
1276 Editor::select_range_between ()
1278 nframes64_t start;
1279 nframes64_t end;
1281 if (!get_edit_op_range (start, end)) {
1282 return;
1285 set_mouse_mode (MouseRange);
1286 selection->set (start, end);
1289 bool
1290 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1292 nframes64_t m;
1293 bool ignored;
1295 /* in range mode, use any existing selection */
1297 if (mouse_mode == MouseRange && !selection->time.empty()) {
1298 /* we know that these are ordered */
1299 start = selection->time.start();
1300 end = selection->time.end_frame();
1301 return true;
1304 if (!mouse_frame (m, ignored)) {
1305 /* mouse is not in a canvas, try playhead+selected marker.
1306 this is probably most true when using menus.
1309 if (selection->markers.empty()) {
1310 return false;
1313 start = selection->markers.front()->position();
1314 end = _session->audible_frame();
1316 } else {
1318 switch (_edit_point) {
1319 case EditAtPlayhead:
1320 if (selection->markers.empty()) {
1321 /* use mouse + playhead */
1322 start = m;
1323 end = _session->audible_frame();
1324 } else {
1325 /* use playhead + selected marker */
1326 start = _session->audible_frame();
1327 end = selection->markers.front()->position();
1329 break;
1331 case EditAtMouse:
1332 /* use mouse + selected marker */
1333 if (selection->markers.empty()) {
1334 start = m;
1335 end = _session->audible_frame();
1336 } else {
1337 start = selection->markers.front()->position();
1338 end = m;
1340 break;
1342 case EditAtSelectedMarker:
1343 /* use mouse + selected marker */
1344 if (selection->markers.empty()) {
1346 MessageDialog win (_("No edit range defined"),
1347 false,
1348 MESSAGE_INFO,
1349 BUTTONS_OK);
1351 win.set_secondary_text (
1352 _("the edit point is Selected Marker\nbut there is no selected marker."));
1355 win.set_default_response (RESPONSE_CLOSE);
1356 win.set_position (Gtk::WIN_POS_MOUSE);
1357 win.show_all();
1359 win.run ();
1361 return false; // NO RANGE
1363 start = selection->markers.front()->position();
1364 end = m;
1365 break;
1369 if (start == end) {
1370 return false;
1373 if (start > end) {
1374 swap (start, end);
1377 /* turn range into one delimited by start...end,
1378 not start...end-1
1381 end++;
1383 return true;
1386 void
1387 Editor::deselect_all ()
1389 selection->clear ();
1392 long
1393 Editor::select_range_around_region (RegionView* rv)
1395 assert (rv);
1397 selection->set (&rv->get_time_axis_view());
1399 selection->time.clear ();
1400 boost::shared_ptr<Region> r = rv->region ();
1401 return selection->set (r->position(), r->position() + r->length());