fix up file renaming code a little bit
[ArdourMidi.git] / gtk2_ardour / editor_selection.cc
blobfdc1fe2ad02511b61bc9efdc79bac191ca8852d8
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);
298 if (route_basis == 0) {
299 return;
302 set<RouteTimeAxisView*> tracks;
303 tracks.insert (route_basis);
305 RouteGroup* group = route_basis->route()->route_group();
307 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
309 /* the basis is a member of an active route group, with the appropriate
310 properties; find other members */
312 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
313 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
314 if (v && v->route()->route_group() == group) {
315 tracks.insert (v);
320 /* call the slots */
321 uint32_t const sz = tracks.size ();
323 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
324 sl (**i, sz);
328 void
329 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
331 boost::shared_ptr<Playlist> pl;
332 vector<boost::shared_ptr<Region> > results;
333 RegionView* marv;
334 boost::shared_ptr<Track> tr;
336 if ((tr = tv.track()) == 0) {
337 /* bus */
338 return;
341 if (&tv == &basis->get_time_axis_view()) {
342 /* looking in same track as the original */
343 return;
346 if ((pl = tr->playlist()) != 0) {
347 pl->get_equivalent_regions (basis->region(), results);
350 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
351 if ((marv = tv.view()->find_view (*ir)) != 0) {
352 all_equivs->push_back (marv);
357 void
358 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
360 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview(), property);
362 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
364 equivalent_regions.push_back (basis);
367 RegionSelection
368 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
370 RegionSelection equivalent;
372 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
374 vector<RegionView*> eq;
376 mapover_tracks (
377 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
378 &(*i)->get_trackview(), prop
381 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
382 equivalent.add (*j);
385 equivalent.add (*i);
388 return equivalent;
393 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
395 int region_count = 0;
397 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
399 RouteTimeAxisView* tatv;
401 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
403 boost::shared_ptr<Playlist> pl;
404 vector<boost::shared_ptr<Region> > results;
405 RegionView* marv;
406 boost::shared_ptr<Track> tr;
408 if ((tr = tatv->track()) == 0) {
409 /* bus */
410 continue;
413 if ((pl = (tr->playlist())) != 0) {
414 pl->get_region_list_equivalent_regions (region, results);
417 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
418 if ((marv = tatv->view()->find_view (*ir)) != 0) {
419 region_count++;
426 return region_count;
430 bool
431 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
433 vector<RegionView*> all_equivalent_regions;
434 bool commit = false;
436 if (!clicked_regionview || !clicked_routeview) {
437 return false;
440 if (press) {
441 button_release_can_deselect = false;
444 if (op == Selection::Toggle || op == Selection::Set) {
447 switch (op) {
448 case Selection::Toggle:
450 if (selection->selected (clicked_regionview)) {
451 if (press) {
453 /* whatever was clicked was selected already; do nothing here but allow
454 the button release to deselect it
457 button_release_can_deselect = true;
459 } else {
461 if (button_release_can_deselect) {
463 /* just remove this one region, but only on a permitted button release */
465 selection->remove (clicked_regionview);
466 commit = true;
468 /* no more deselect action on button release till a new press
469 finds an already selected object.
472 button_release_can_deselect = false;
476 } else {
478 if (press) {
480 if (selection->selected (clicked_routeview)) {
481 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
482 } else {
483 all_equivalent_regions.push_back (clicked_regionview);
486 /* add all the equivalent regions, but only on button press */
488 if (!all_equivalent_regions.empty()) {
489 commit = true;
492 selection->add (all_equivalent_regions);
495 break;
497 case Selection::Set:
498 if (!selection->selected (clicked_regionview)) {
499 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
500 selection->set (all_equivalent_regions);
501 commit = true;
502 } else {
503 /* no commit necessary: clicked on an already selected region */
504 goto out;
506 break;
508 default:
509 /* silly compiler */
510 break;
513 } else if (op == Selection::Extend) {
515 list<Selectable*> results;
516 nframes64_t last_frame;
517 nframes64_t first_frame;
518 bool same_track = false;
520 /* 1. find the last selected regionview in the track that was clicked in */
522 last_frame = 0;
523 first_frame = max_frames;
525 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
526 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
528 if ((*x)->region()->last_frame() > last_frame) {
529 last_frame = (*x)->region()->last_frame();
532 if ((*x)->region()->first_frame() < first_frame) {
533 first_frame = (*x)->region()->first_frame();
536 same_track = true;
540 if (same_track) {
542 /* 2. figure out the boundaries for our search for new objects */
544 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
545 case OverlapNone:
546 if (last_frame < clicked_regionview->region()->first_frame()) {
547 first_frame = last_frame;
548 last_frame = clicked_regionview->region()->last_frame();
549 } else {
550 last_frame = first_frame;
551 first_frame = clicked_regionview->region()->first_frame();
553 break;
555 case OverlapExternal:
556 if (last_frame < clicked_regionview->region()->first_frame()) {
557 first_frame = last_frame;
558 last_frame = clicked_regionview->region()->last_frame();
559 } else {
560 last_frame = first_frame;
561 first_frame = clicked_regionview->region()->first_frame();
563 break;
565 case OverlapInternal:
566 if (last_frame < clicked_regionview->region()->first_frame()) {
567 first_frame = last_frame;
568 last_frame = clicked_regionview->region()->last_frame();
569 } else {
570 last_frame = first_frame;
571 first_frame = clicked_regionview->region()->first_frame();
573 break;
575 case OverlapStart:
576 case OverlapEnd:
577 /* nothing to do except add clicked region to selection, since it
578 overlaps with the existing selection in this track.
580 break;
583 } else {
585 /* click in a track that has no regions selected, so extend vertically
586 to pick out all regions that are defined by the existing selection
587 plus this one.
591 first_frame = entered_regionview->region()->position();
592 last_frame = entered_regionview->region()->last_frame();
594 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
595 if ((*i)->region()->position() < first_frame) {
596 first_frame = (*i)->region()->position();
598 if ((*i)->region()->last_frame() + 1 > last_frame) {
599 last_frame = (*i)->region()->last_frame();
604 /* 2. find all the tracks we should select in */
606 set<RouteTimeAxisView*> relevant_tracks;
608 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
609 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
610 if (r) {
611 relevant_tracks.insert (r);
615 set<RouteTimeAxisView*> already_in_selection;
617 if (relevant_tracks.empty()) {
619 /* no tracks selected .. thus .. if the
620 regionview we're in isn't selected
621 (i.e. we're about to extend to it), then
622 find all tracks between the this one and
623 any selected ones.
626 if (!selection->selected (entered_regionview)) {
628 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
630 if (rtv) {
632 /* add this track to the ones we will search */
634 relevant_tracks.insert (rtv);
636 /* find the track closest to this one that
637 already a selected region.
640 RouteTimeAxisView* closest = 0;
641 int distance = INT_MAX;
642 int key = rtv->route()->order_key ("editor");
644 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
646 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
648 if (artv && artv != rtv) {
650 pair<set<RouteTimeAxisView*>::iterator,bool> result;
652 result = already_in_selection.insert (artv);
654 if (result.second) {
655 /* newly added to already_in_selection */
657 int d = artv->route()->order_key ("editor");
659 d -= key;
661 if (abs (d) < distance) {
662 distance = abs (d);
663 closest = artv;
669 if (closest) {
671 /* now add all tracks between that one and this one */
673 int okey = closest->route()->order_key ("editor");
675 if (okey > key) {
676 swap (okey, key);
679 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
680 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
681 if (artv && artv != rtv) {
683 int k = artv->route()->order_key ("editor");
685 if (k >= okey && k <= key) {
687 /* in range but don't add it if
688 it already has tracks selected.
689 this avoids odd selection
690 behaviour that feels wrong.
693 if (find (already_in_selection.begin(),
694 already_in_selection.end(),
695 artv) == already_in_selection.end()) {
697 relevant_tracks.insert (artv);
707 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
708 one that was clicked.
711 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
712 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
715 /* 4. convert to a vector of regions */
717 vector<RegionView*> regions;
719 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
720 RegionView* arv;
722 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
723 regions.push_back (arv);
727 if (!regions.empty()) {
728 selection->add (regions);
729 commit = true;
733 out:
734 return commit;
738 void
739 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
741 vector<RegionView*> all_equivalent_regions;
743 get_regions_corresponding_to (region, all_equivalent_regions);
745 if (all_equivalent_regions.empty()) {
746 return;
749 begin_reversible_command (_("set selected regions"));
751 switch (op) {
752 case Selection::Toggle:
753 /* XXX this is not correct */
754 selection->toggle (all_equivalent_regions);
755 break;
756 case Selection::Set:
757 selection->set (all_equivalent_regions);
758 break;
759 case Selection::Extend:
760 selection->add (all_equivalent_regions);
761 break;
762 case Selection::Add:
763 selection->add (all_equivalent_regions);
764 break;
767 commit_reversible_command () ;
770 bool
771 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
773 RegionView* rv;
774 boost::shared_ptr<Region> r (weak_r.lock());
776 if (!r) {
777 return true;
780 if ((rv = sv->find_view (r)) == 0) {
781 return true;
784 /* don't reset the selection if its something other than
785 a single other region.
788 if (selection->regions.size() > 1) {
789 return true;
792 begin_reversible_command (_("set selected regions"));
794 selection->set (rv);
796 commit_reversible_command () ;
798 return true;
801 void
802 Editor::track_selection_changed ()
804 switch (selection->tracks.size()){
805 case 0:
806 break;
807 default:
808 set_selected_mixer_strip (*(selection->tracks.front()));
809 break;
812 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
813 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
814 (*i)->set_selected (true);
815 } else {
816 (*i)->set_selected (false);
820 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
823 void
824 Editor::time_selection_changed ()
826 if (Profile->get_sae()) {
827 return;
830 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
831 (*i)->hide_selection ();
834 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
835 (*i)->show_selection (selection->time);
838 if (selection->time.empty()) {
839 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
840 } else {
841 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
845 void
846 Editor::sensitize_the_right_region_actions (bool have_selected_regions)
848 for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
849 x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
851 string accel_path = (*x)->get_accel_path ();
852 AccelKey key;
854 /* if there is an accelerator, it should always be sensitive
855 to allow for keyboard ops on entered regions.
858 bool known = ActionManager::lookup_entry (accel_path, key);
860 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
861 (*x)->set_sensitive (true);
862 } else {
863 (*x)->set_sensitive (have_selected_regions);
869 void
870 Editor::region_selection_changed ()
872 _regions->block_change_connection (true);
873 editor_regions_selection_changed_connection.block(true);
875 _regions->unselect_all ();
877 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
878 (*i)->set_selected_regionviews (selection->regions);
881 _regions->set_selected (selection->regions);
883 sensitize_the_right_region_actions (!selection->regions.empty());
885 _regions->block_change_connection (false);
886 editor_regions_selection_changed_connection.block(false);
889 void
890 Editor::point_selection_changed ()
892 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
893 (*i)->set_selected_points (selection->points);
897 void
898 Editor::select_all_in_track (Selection::Operation op)
900 list<Selectable *> touched;
902 if (!clicked_routeview) {
903 return;
906 clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
908 switch (op) {
909 case Selection::Toggle:
910 selection->add (touched);
911 break;
912 case Selection::Set:
913 selection->set (touched);
914 break;
915 case Selection::Extend:
916 /* meaningless, because we're selecting everything */
917 break;
918 case Selection::Add:
919 selection->add (touched);
920 break;
924 void
925 Editor::select_all (Selection::Operation op)
927 list<Selectable *> touched;
929 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
930 if ((*iter)->hidden()) {
931 continue;
933 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
935 begin_reversible_command (_("select all"));
936 switch (op) {
937 case Selection::Add:
938 selection->add (touched);
939 break;
940 case Selection::Toggle:
941 selection->add (touched);
942 break;
943 case Selection::Set:
944 selection->set (touched);
945 break;
946 case Selection::Extend:
947 /* meaningless, because we're selecting everything */
948 break;
950 commit_reversible_command ();
952 void
953 Editor::invert_selection_in_track ()
955 list<Selectable *> touched;
957 if (!clicked_routeview) {
958 return;
961 clicked_routeview->get_inverted_selectables (*selection, touched);
962 selection->set (touched);
965 void
966 Editor::invert_selection ()
968 list<Selectable *> touched;
970 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
971 if ((*iter)->hidden()) {
972 continue;
974 (*iter)->get_inverted_selectables (*selection, touched);
977 selection->set (touched);
980 /** @param top Top (lower) y limit in trackview coordinates.
981 * @param bottom Bottom (higher) y limit in trackview coordinates.
983 bool
984 Editor::select_all_within (nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op)
986 list<Selectable*> found;
988 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
990 if ((*iter)->hidden()) {
991 continue;
994 (*iter)->get_selectables (start, end, top, bot, found);
997 if (found.empty()) {
998 return false;
1001 begin_reversible_command (_("select all within"));
1002 switch (op) {
1003 case Selection::Add:
1004 selection->add (found);
1005 break;
1006 case Selection::Toggle:
1007 selection->toggle (found);
1008 break;
1009 case Selection::Set:
1010 selection->set (found);
1011 break;
1012 case Selection::Extend:
1013 /* not defined yet */
1014 break;
1017 commit_reversible_command ();
1019 return !found.empty();
1022 void
1023 Editor::set_selection_from_region ()
1025 if (selection->regions.empty()) {
1026 return;
1029 selection->set (selection->regions.start(), selection->regions.end_frame());
1030 if (!Profile->get_sae()) {
1031 set_mouse_mode (Editing::MouseRange, false);
1035 void
1036 Editor::set_selection_from_punch()
1038 Location* location;
1040 if ((location = _session->locations()->auto_punch_location()) == 0) {
1041 return;
1044 set_selection_from_range (*location);
1047 void
1048 Editor::set_selection_from_loop()
1050 Location* location;
1052 if ((location = _session->locations()->auto_loop_location()) == 0) {
1053 return;
1055 set_selection_from_range (*location);
1058 void
1059 Editor::set_selection_from_range (Location& loc)
1061 begin_reversible_command (_("set selection from range"));
1062 selection->set (loc.start(), loc.end());
1063 commit_reversible_command ();
1065 if (!Profile->get_sae()) {
1066 set_mouse_mode (Editing::MouseRange, false);
1070 void
1071 Editor::select_all_selectables_using_time_selection ()
1073 list<Selectable *> touched;
1075 if (selection->time.empty()) {
1076 return;
1079 nframes64_t start = selection->time[clicked_selection].start;
1080 nframes64_t end = selection->time[clicked_selection].end;
1082 if (end - start < 1) {
1083 return;
1086 TrackViewList* ts;
1088 if (selection->tracks.empty()) {
1089 ts = &track_views;
1090 } else {
1091 ts = &selection->tracks;
1094 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1095 if ((*iter)->hidden()) {
1096 continue;
1098 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1101 begin_reversible_command (_("select all from range"));
1102 selection->set (touched);
1103 commit_reversible_command ();
1107 void
1108 Editor::select_all_selectables_using_punch()
1110 Location* location = _session->locations()->auto_punch_location();
1111 list<Selectable *> touched;
1113 if (location == 0 || (location->end() - location->start() <= 1)) {
1114 return;
1118 TrackViewList* ts;
1120 if (selection->tracks.empty()) {
1121 ts = &track_views;
1122 } else {
1123 ts = &selection->tracks;
1126 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1127 if ((*iter)->hidden()) {
1128 continue;
1130 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1132 begin_reversible_command (_("select all from punch"));
1133 selection->set (touched);
1134 commit_reversible_command ();
1138 void
1139 Editor::select_all_selectables_using_loop()
1141 Location* location = _session->locations()->auto_loop_location();
1142 list<Selectable *> touched;
1144 if (location == 0 || (location->end() - location->start() <= 1)) {
1145 return;
1149 TrackViewList* ts;
1151 if (selection->tracks.empty()) {
1152 ts = &track_views;
1153 } else {
1154 ts = &selection->tracks;
1157 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1158 if ((*iter)->hidden()) {
1159 continue;
1161 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1163 begin_reversible_command (_("select all from loop"));
1164 selection->set (touched);
1165 commit_reversible_command ();
1169 void
1170 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1172 nframes64_t start;
1173 nframes64_t end;
1174 list<Selectable *> touched;
1176 if (after) {
1177 begin_reversible_command (_("select all after cursor"));
1178 start = cursor->current_frame ;
1179 end = _session->current_end_frame();
1180 } else {
1181 if (cursor->current_frame > 0) {
1182 begin_reversible_command (_("select all before cursor"));
1183 start = 0;
1184 end = cursor->current_frame - 1;
1185 } else {
1186 return;
1191 TrackViewList* ts;
1193 if (selection->tracks.empty()) {
1194 ts = &track_views;
1195 } else {
1196 ts = &selection->tracks;
1199 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1200 if ((*iter)->hidden()) {
1201 continue;
1203 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1205 selection->set (touched);
1206 commit_reversible_command ();
1209 void
1210 Editor::select_all_selectables_using_edit (bool after)
1212 nframes64_t start;
1213 nframes64_t end;
1214 list<Selectable *> touched;
1216 if (after) {
1217 begin_reversible_command (_("select all after edit"));
1218 start = get_preferred_edit_position();
1219 end = _session->current_end_frame();
1220 } else {
1221 if ((end = get_preferred_edit_position()) > 1) {
1222 begin_reversible_command (_("select all before edit"));
1223 start = 0;
1224 end -= 1;
1225 } else {
1226 return;
1231 TrackViewList* 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);
1245 selection->set (touched);
1246 commit_reversible_command ();
1249 void
1250 Editor::select_all_selectables_between (bool /*within*/)
1252 nframes64_t start;
1253 nframes64_t end;
1254 list<Selectable *> touched;
1256 if (!get_edit_op_range (start, end)) {
1257 return;
1260 TrackViewList* ts;
1262 if (selection->tracks.empty()) {
1263 ts = &track_views;
1264 } else {
1265 ts = &selection->tracks;
1268 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1269 if ((*iter)->hidden()) {
1270 continue;
1272 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1275 selection->set (touched);
1278 void
1279 Editor::select_range_between ()
1281 nframes64_t start;
1282 nframes64_t end;
1284 if (!get_edit_op_range (start, end)) {
1285 return;
1288 set_mouse_mode (MouseRange);
1289 selection->set (start, end);
1292 bool
1293 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1295 nframes64_t m;
1296 bool ignored;
1298 /* in range mode, use any existing selection */
1300 if (mouse_mode == MouseRange && !selection->time.empty()) {
1301 /* we know that these are ordered */
1302 start = selection->time.start();
1303 end = selection->time.end_frame();
1304 return true;
1307 if (!mouse_frame (m, ignored)) {
1308 /* mouse is not in a canvas, try playhead+selected marker.
1309 this is probably most true when using menus.
1312 if (selection->markers.empty()) {
1313 return false;
1316 start = selection->markers.front()->position();
1317 end = _session->audible_frame();
1319 } else {
1321 switch (_edit_point) {
1322 case EditAtPlayhead:
1323 if (selection->markers.empty()) {
1324 /* use mouse + playhead */
1325 start = m;
1326 end = _session->audible_frame();
1327 } else {
1328 /* use playhead + selected marker */
1329 start = _session->audible_frame();
1330 end = selection->markers.front()->position();
1332 break;
1334 case EditAtMouse:
1335 /* use mouse + selected marker */
1336 if (selection->markers.empty()) {
1337 start = m;
1338 end = _session->audible_frame();
1339 } else {
1340 start = selection->markers.front()->position();
1341 end = m;
1343 break;
1345 case EditAtSelectedMarker:
1346 /* use mouse + selected marker */
1347 if (selection->markers.empty()) {
1349 MessageDialog win (_("No edit range defined"),
1350 false,
1351 MESSAGE_INFO,
1352 BUTTONS_OK);
1354 win.set_secondary_text (
1355 _("the edit point is Selected Marker\nbut there is no selected marker."));
1358 win.set_default_response (RESPONSE_CLOSE);
1359 win.set_position (Gtk::WIN_POS_MOUSE);
1360 win.show_all();
1362 win.run ();
1364 return false; // NO RANGE
1366 start = selection->markers.front()->position();
1367 end = m;
1368 break;
1372 if (start == end) {
1373 return false;
1376 if (start > end) {
1377 swap (start, end);
1380 /* turn range into one delimited by start...end,
1381 not start...end-1
1384 end++;
1386 return true;
1389 void
1390 Editor::deselect_all ()
1392 selection->clear ();
1395 long
1396 Editor::select_range_around_region (RegionView* rv)
1398 assert (rv);
1400 selection->set (&rv->get_time_axis_view());
1402 selection->time.clear ();
1403 boost::shared_ptr<Region> r = rv->region ();
1404 return selection->set (r->position(), r->position() + r->length());