put the issue of using a monitor section into ~/.config/ardour.rc, not the session...
[ardour2.git] / gtk2_ardour / editor_selection.cc
blob5b29d8439f99ecacc24e0499434069daae1fb659
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/diskstream.h"
27 #include "ardour/playlist.h"
28 #include "ardour/route_group.h"
29 #include "ardour/profile.h"
31 #include "editor.h"
32 #include "actions.h"
33 #include "audio_time_axis.h"
34 #include "audio_region_view.h"
35 #include "audio_streamview.h"
36 #include "automation_line.h"
37 #include "control_point.h"
38 #include "editor_regions.h"
40 #include "i18n.h"
42 using namespace std;
43 using namespace ARDOUR;
44 using namespace PBD;
45 using namespace Gtk;
46 using namespace Glib;
47 using namespace Gtkmm2ext;
48 using namespace Editing;
50 struct TrackViewByPositionSorter
52 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
53 return a->y_position() < b->y_position();
57 bool
58 Editor::extend_selection_to_track (TimeAxisView& view)
60 if (selection->selected (&view)) {
61 /* already selected, do nothing */
62 return false;
65 if (selection->tracks.empty()) {
67 if (!selection->selected (&view)) {
68 selection->set (&view);
69 return true;
70 } else {
71 return false;
75 /* something is already selected, so figure out which range of things to add */
77 TrackViewList to_be_added;
78 TrackViewList sorted = track_views;
79 TrackViewByPositionSorter cmp;
80 bool passed_clicked = false;
81 bool forwards = true;
83 sorted.sort (cmp);
85 if (!selection->selected (&view)) {
86 to_be_added.push_back (&view);
89 /* figure out if we should go forward or backwards */
91 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
93 if ((*i) == &view) {
94 passed_clicked = true;
97 if (selection->selected (*i)) {
98 if (passed_clicked) {
99 forwards = true;
100 } else {
101 forwards = false;
103 break;
107 passed_clicked = false;
109 if (forwards) {
111 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
113 if ((*i) == &view) {
114 passed_clicked = true;
115 continue;
118 if (passed_clicked) {
119 if ((*i)->hidden()) {
120 continue;
122 if (selection->selected (*i)) {
123 break;
124 } else if (!(*i)->hidden()) {
125 to_be_added.push_back (*i);
130 } else {
132 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
134 if ((*r) == &view) {
135 passed_clicked = true;
136 continue;
139 if (passed_clicked) {
141 if ((*r)->hidden()) {
142 continue;
145 if (selection->selected (*r)) {
146 break;
147 } else if (!(*r)->hidden()) {
148 to_be_added.push_back (*r);
154 if (!to_be_added.empty()) {
155 selection->add (to_be_added);
156 return true;
159 return false;
162 void
163 Editor::select_all_tracks ()
165 TrackViewList visible_views;
166 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
167 if ((*i)->marked_for_display()) {
168 visible_views.push_back (*i);
171 selection->set (visible_views);
174 /** Select clicked_routeview, unless there are no currently selected
175 * tracks, in which case nothing will happen unless `force' is true.
177 void
178 Editor::set_selected_track_as_side_effect (bool force)
180 if (!clicked_routeview) {
181 return;
184 if (!selection->tracks.empty()) {
185 if (!selection->selected (clicked_routeview)) {
186 selection->add (clicked_routeview);
189 } else if (force) {
190 selection->set (clicked_routeview);
194 void
195 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
197 switch (op) {
198 case Selection::Toggle:
199 if (selection->selected (&view)) {
200 if (!no_remove) {
201 selection->remove (&view);
203 } else {
204 selection->add (&view);
206 break;
208 case Selection::Add:
209 if (!selection->selected (&view)) {
210 selection->add (&view);
212 break;
214 case Selection::Set:
215 selection->set (&view);
216 break;
218 case Selection::Extend:
219 extend_selection_to_track (view);
220 break;
224 void
225 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
227 if (!clicked_routeview) {
228 return;
231 if (!press) {
232 return;
235 set_selected_track (*clicked_routeview, op, no_remove);
238 bool
239 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
241 if (!clicked_control_point) {
242 return false;
245 if (clicked_control_point->selected()) {
246 /* the clicked control point is already selected; others may be as well, so
247 don't change the selection.
249 return true;
252 /* We know the ControlPoint that was clicked, but (as discussed in automation_selectable.h)
253 * selected automation data are described by areas on the AutomationLine. A ControlPoint
254 * represents any model points in the space that it takes up, so the AutomationSelectable
255 * needs to be the size of the ControlPoint.
258 double const size = clicked_control_point->size ();
260 nframes64_t const x1 = pixel_to_frame (clicked_control_point->get_x() - size / 2);
261 nframes64_t const x2 = pixel_to_frame (clicked_control_point->get_x() + size / 2);
262 double y1 = clicked_control_point->get_y() - size / 2;
263 double y2 = clicked_control_point->get_y() + size / 2;
265 /* convert the y values to trackview space */
266 double dummy = 0;
267 clicked_control_point->line().parent_group().i2w (dummy, y1);
268 clicked_control_point->line().parent_group().i2w (dummy, y2);
269 _trackview_group->w2i (dummy, y1);
270 _trackview_group->w2i (dummy, y2);
272 /* and set up the selection */
273 return select_all_within (x1, x2, y1, y2, selection->tracks, Selection::Set);
276 void
277 Editor::get_onscreen_tracks (TrackViewList& tvl)
279 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
280 if ((*i)->y_position() < _canvas_height) {
281 tvl.push_back (*i);
286 /** Call a slot for a given `basis' track and also for any track that is in the same
287 * active route group with a particular set of properties.
289 * @param sl Slot to call.
290 * @param basis Basis track.
291 * @param prop Properties that active edit groups must share to be included in the map.
294 void
295 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
297 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();
306 if (group && group->enabled_property (prop)) {
308 /* the basis is a member of an active route group, with the appropriate
309 properties; find other members */
311 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
312 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
313 if (v && v->route()->route_group() == group) {
314 tracks.insert (v);
319 /* call the slots */
320 uint32_t const sz = tracks.size ();
321 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
322 sl (**i, sz);
326 void
327 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
329 boost::shared_ptr<Playlist> pl;
330 vector<boost::shared_ptr<Region> > results;
331 RegionView* marv;
332 boost::shared_ptr<Diskstream> ds;
334 if ((ds = tv.get_diskstream()) == 0) {
335 /* bus */
336 return;
339 if (&tv == &basis->get_time_axis_view()) {
340 /* looking in same track as the original */
341 return;
344 if ((pl = ds->playlist()) != 0) {
345 pl->get_equivalent_regions (basis->region(), results);
348 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
349 if ((marv = tv.view()->find_view (*ir)) != 0) {
350 all_equivs->push_back (marv);
355 void
356 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
358 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_trackview(), property);
360 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
362 equivalent_regions.push_back (basis);
365 RegionSelection
366 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
368 RegionSelection equivalent;
370 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
372 vector<RegionView*> eq;
374 mapover_tracks (
375 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
376 &(*i)->get_trackview(), prop
379 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
380 equivalent.add (*j);
383 equivalent.add (*i);
386 return equivalent;
391 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
393 int region_count = 0;
395 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
397 RouteTimeAxisView* tatv;
399 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
401 boost::shared_ptr<Playlist> pl;
402 vector<boost::shared_ptr<Region> > results;
403 RegionView* marv;
404 boost::shared_ptr<Diskstream> ds;
406 if ((ds = tatv->get_diskstream()) == 0) {
407 /* bus */
408 continue;
411 if ((pl = (ds->playlist())) != 0) {
412 pl->get_region_list_equivalent_regions (region, results);
415 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
416 if ((marv = tatv->view()->find_view (*ir)) != 0) {
417 region_count++;
424 return region_count;
428 bool
429 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
431 vector<RegionView*> all_equivalent_regions;
432 bool commit = false;
434 if (!clicked_regionview || !clicked_routeview) {
435 return false;
438 if (press) {
439 button_release_can_deselect = false;
442 if (op == Selection::Toggle || op == Selection::Set) {
445 switch (op) {
446 case Selection::Toggle:
448 if (selection->selected (clicked_regionview)) {
449 if (press) {
451 /* whatever was clicked was selected already; do nothing here but allow
452 the button release to deselect it
455 button_release_can_deselect = true;
457 } else {
459 if (button_release_can_deselect) {
461 /* just remove this one region, but only on a permitted button release */
463 selection->remove (clicked_regionview);
464 commit = true;
466 /* no more deselect action on button release till a new press
467 finds an already selected object.
470 button_release_can_deselect = false;
474 } else {
476 if (press) {
478 if (selection->selected (clicked_routeview)) {
479 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
480 } else {
481 all_equivalent_regions.push_back (clicked_regionview);
484 /* add all the equivalent regions, but only on button press */
486 if (!all_equivalent_regions.empty()) {
487 commit = true;
490 selection->add (all_equivalent_regions);
493 break;
495 case Selection::Set:
496 if (!selection->selected (clicked_regionview)) {
497 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
498 selection->set (all_equivalent_regions);
499 commit = true;
500 } else {
501 /* no commit necessary: clicked on an already selected region */
502 goto out;
504 break;
506 default:
507 /* silly compiler */
508 break;
511 } else if (op == Selection::Extend) {
513 list<Selectable*> results;
514 nframes64_t last_frame;
515 nframes64_t first_frame;
516 bool same_track = false;
518 /* 1. find the last selected regionview in the track that was clicked in */
520 last_frame = 0;
521 first_frame = max_frames;
523 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
524 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
526 if ((*x)->region()->last_frame() > last_frame) {
527 last_frame = (*x)->region()->last_frame();
530 if ((*x)->region()->first_frame() < first_frame) {
531 first_frame = (*x)->region()->first_frame();
534 same_track = true;
538 if (same_track) {
540 /* 2. figure out the boundaries for our search for new objects */
542 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
543 case OverlapNone:
544 if (last_frame < clicked_regionview->region()->first_frame()) {
545 first_frame = last_frame;
546 last_frame = clicked_regionview->region()->last_frame();
547 } else {
548 last_frame = first_frame;
549 first_frame = clicked_regionview->region()->first_frame();
551 break;
553 case OverlapExternal:
554 if (last_frame < clicked_regionview->region()->first_frame()) {
555 first_frame = last_frame;
556 last_frame = clicked_regionview->region()->last_frame();
557 } else {
558 last_frame = first_frame;
559 first_frame = clicked_regionview->region()->first_frame();
561 break;
563 case OverlapInternal:
564 if (last_frame < clicked_regionview->region()->first_frame()) {
565 first_frame = last_frame;
566 last_frame = clicked_regionview->region()->last_frame();
567 } else {
568 last_frame = first_frame;
569 first_frame = clicked_regionview->region()->first_frame();
571 break;
573 case OverlapStart:
574 case OverlapEnd:
575 /* nothing to do except add clicked region to selection, since it
576 overlaps with the existing selection in this track.
578 break;
581 } else {
583 /* click in a track that has no regions selected, so extend vertically
584 to pick out all regions that are defined by the existing selection
585 plus this one.
589 first_frame = entered_regionview->region()->position();
590 last_frame = entered_regionview->region()->last_frame();
592 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
593 if ((*i)->region()->position() < first_frame) {
594 first_frame = (*i)->region()->position();
596 if ((*i)->region()->last_frame() + 1 > last_frame) {
597 last_frame = (*i)->region()->last_frame();
602 /* 2. find all the tracks we should select in */
604 set<RouteTimeAxisView*> relevant_tracks;
606 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
607 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
608 if (r) {
609 relevant_tracks.insert (r);
613 set<RouteTimeAxisView*> already_in_selection;
615 if (relevant_tracks.empty()) {
617 /* no tracks selected .. thus .. if the
618 regionview we're in isn't selected
619 (i.e. we're about to extend to it), then
620 find all tracks between the this one and
621 any selected ones.
624 if (!selection->selected (entered_regionview)) {
626 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&entered_regionview->get_time_axis_view());
628 if (rtv) {
630 /* add this track to the ones we will search */
632 relevant_tracks.insert (rtv);
634 /* find the track closest to this one that
635 already a selected region.
638 RouteTimeAxisView* closest = 0;
639 int distance = INT_MAX;
640 int key = rtv->route()->order_key ("editor");
642 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
644 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
646 if (artv && artv != rtv) {
648 pair<set<RouteTimeAxisView*>::iterator,bool> result;
650 result = already_in_selection.insert (artv);
652 if (result.second) {
653 /* newly added to already_in_selection */
655 int d = artv->route()->order_key ("editor");
657 d -= key;
659 if (abs (d) < distance) {
660 distance = abs (d);
661 closest = artv;
667 if (closest) {
669 /* now add all tracks between that one and this one */
671 int okey = closest->route()->order_key ("editor");
673 if (okey > key) {
674 swap (okey, key);
677 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
678 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
679 if (artv && artv != rtv) {
681 int k = artv->route()->order_key ("editor");
683 if (k >= okey && k <= key) {
685 /* in range but don't add it if
686 it already has tracks selected.
687 this avoids odd selection
688 behaviour that feels wrong.
691 if (find (already_in_selection.begin(),
692 already_in_selection.end(),
693 artv) == already_in_selection.end()) {
695 relevant_tracks.insert (artv);
705 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
706 one that was clicked.
709 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
710 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
713 /* 4. convert to a vector of regions */
715 vector<RegionView*> regions;
717 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
718 RegionView* arv;
720 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
721 regions.push_back (arv);
725 if (!regions.empty()) {
726 selection->add (regions);
727 commit = true;
731 out:
732 return commit;
736 void
737 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
739 vector<RegionView*> all_equivalent_regions;
741 get_regions_corresponding_to (region, all_equivalent_regions);
743 if (all_equivalent_regions.empty()) {
744 return;
747 begin_reversible_command (_("set selected regions"));
749 switch (op) {
750 case Selection::Toggle:
751 /* XXX this is not correct */
752 selection->toggle (all_equivalent_regions);
753 break;
754 case Selection::Set:
755 selection->set (all_equivalent_regions);
756 break;
757 case Selection::Extend:
758 selection->add (all_equivalent_regions);
759 break;
760 case Selection::Add:
761 selection->add (all_equivalent_regions);
762 break;
765 commit_reversible_command () ;
768 bool
769 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
771 RegionView* rv;
772 boost::shared_ptr<Region> r (weak_r.lock());
774 if (!r) {
775 return true;
778 if ((rv = sv->find_view (r)) == 0) {
779 return true;
782 /* don't reset the selection if its something other than
783 a single other region.
786 if (selection->regions.size() > 1) {
787 return true;
790 begin_reversible_command (_("set selected regions"));
792 selection->set (rv);
794 commit_reversible_command () ;
796 return true;
799 void
800 Editor::track_selection_changed ()
802 switch (selection->tracks.size()){
803 case 0:
804 break;
805 default:
806 set_selected_mixer_strip (*(selection->tracks.front()));
807 break;
810 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
811 if (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end()) {
812 (*i)->set_selected (true);
813 } else {
814 (*i)->set_selected (false);
818 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
821 void
822 Editor::time_selection_changed ()
824 if (Profile->get_sae()) {
825 return;
828 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
829 (*i)->hide_selection ();
832 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
833 (*i)->show_selection (selection->time);
836 if (selection->time.empty()) {
837 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
838 } else {
839 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
843 void
844 Editor::sensitize_the_right_region_actions (bool have_selected_regions)
846 for (vector<Glib::RefPtr<Action> >::iterator x = ActionManager::region_selection_sensitive_actions.begin();
847 x != ActionManager::region_selection_sensitive_actions.end(); ++x) {
849 string accel_path = (*x)->get_accel_path ();
850 AccelKey key;
852 /* if there is an accelerator, it should always be sensitive
853 to allow for keyboard ops on entered regions.
856 bool known = ActionManager::lookup_entry (accel_path, key);
858 if (known && ((key.get_key() != GDK_VoidSymbol) && (key.get_key() != 0))) {
859 (*x)->set_sensitive (true);
860 } else {
861 (*x)->set_sensitive (have_selected_regions);
867 void
868 Editor::region_selection_changed ()
870 _regions->block_change_connection (true);
871 editor_regions_selection_changed_connection.block(true);
873 _regions->unselect_all ();
875 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
876 (*i)->set_selected_regionviews (selection->regions);
879 _regions->set_selected (selection->regions);
881 sensitize_the_right_region_actions (!selection->regions.empty());
883 _regions->block_change_connection (false);
884 editor_regions_selection_changed_connection.block(false);
887 void
888 Editor::point_selection_changed ()
890 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
891 (*i)->set_selected_points (selection->points);
895 void
896 Editor::select_all_in_track (Selection::Operation op)
898 list<Selectable *> touched;
900 if (!clicked_routeview) {
901 return;
904 clicked_routeview->get_selectables (0, max_frames, 0, DBL_MAX, touched);
906 switch (op) {
907 case Selection::Toggle:
908 selection->add (touched);
909 break;
910 case Selection::Set:
911 selection->set (touched);
912 break;
913 case Selection::Extend:
914 /* meaningless, because we're selecting everything */
915 break;
916 case Selection::Add:
917 selection->add (touched);
918 break;
922 void
923 Editor::select_all (Selection::Operation op)
925 list<Selectable *> touched;
927 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
928 if ((*iter)->hidden()) {
929 continue;
931 (*iter)->get_selectables (0, max_frames, 0, DBL_MAX, touched);
933 begin_reversible_command (_("select all"));
934 switch (op) {
935 case Selection::Add:
936 selection->add (touched);
937 break;
938 case Selection::Toggle:
939 selection->add (touched);
940 break;
941 case Selection::Set:
942 selection->set (touched);
943 break;
944 case Selection::Extend:
945 /* meaningless, because we're selecting everything */
946 break;
948 commit_reversible_command ();
950 void
951 Editor::invert_selection_in_track ()
953 list<Selectable *> touched;
955 if (!clicked_routeview) {
956 return;
959 clicked_routeview->get_inverted_selectables (*selection, touched);
960 selection->set (touched);
963 void
964 Editor::invert_selection ()
966 list<Selectable *> touched;
968 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
969 if ((*iter)->hidden()) {
970 continue;
972 (*iter)->get_inverted_selectables (*selection, touched);
975 selection->set (touched);
978 /** @param top Top (lower) y limit in trackview coordinates.
979 * @param bottom Bottom (higher) y limit in trackview coordinates.
981 bool
982 Editor::select_all_within (
983 nframes64_t start, nframes64_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op
986 list<Selectable*> found;
987 TrackViewList tracks;
989 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
991 if ((*iter)->hidden()) {
992 continue;
995 list<Selectable*>::size_type const n = found.size ();
997 (*iter)->get_selectables (start, end, top, bot, found);
999 if (n != found.size()) {
1000 tracks.push_back (*iter);
1004 if (found.empty()) {
1005 return false;
1008 if (!tracks.empty()) {
1010 switch (op) {
1011 case Selection::Add:
1012 selection->add (tracks);
1013 break;
1014 case Selection::Toggle:
1015 selection->toggle (tracks);
1016 break;
1017 case Selection::Set:
1018 selection->set (tracks);
1019 break;
1020 case Selection::Extend:
1021 /* not defined yet */
1022 break;
1026 begin_reversible_command (_("select all within"));
1027 switch (op) {
1028 case Selection::Add:
1029 selection->add (found);
1030 break;
1031 case Selection::Toggle:
1032 selection->toggle (found);
1033 break;
1034 case Selection::Set:
1035 selection->set (found);
1036 break;
1037 case Selection::Extend:
1038 /* not defined yet */
1039 break;
1042 commit_reversible_command ();
1044 return !found.empty();
1047 void
1048 Editor::set_selection_from_region ()
1050 if (selection->regions.empty()) {
1051 return;
1054 selection->set (selection->regions.start(), selection->regions.end_frame());
1055 if (!Profile->get_sae()) {
1056 set_mouse_mode (Editing::MouseRange, false);
1060 void
1061 Editor::set_selection_from_punch()
1063 Location* location;
1065 if ((location = _session->locations()->auto_punch_location()) == 0) {
1066 return;
1069 set_selection_from_range (*location);
1072 void
1073 Editor::set_selection_from_loop()
1075 Location* location;
1077 if ((location = _session->locations()->auto_loop_location()) == 0) {
1078 return;
1080 set_selection_from_range (*location);
1083 void
1084 Editor::set_selection_from_range (Location& loc)
1086 begin_reversible_command (_("set selection from range"));
1087 selection->set (loc.start(), loc.end());
1088 commit_reversible_command ();
1090 if (!Profile->get_sae()) {
1091 set_mouse_mode (Editing::MouseRange, false);
1095 void
1096 Editor::select_all_selectables_using_time_selection ()
1098 list<Selectable *> touched;
1100 if (selection->time.empty()) {
1101 return;
1104 nframes64_t start = selection->time[clicked_selection].start;
1105 nframes64_t end = selection->time[clicked_selection].end;
1107 if (end - start < 1) {
1108 return;
1111 TrackViewList* ts;
1113 if (selection->tracks.empty()) {
1114 ts = &track_views;
1115 } else {
1116 ts = &selection->tracks;
1119 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1120 if ((*iter)->hidden()) {
1121 continue;
1123 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1126 begin_reversible_command (_("select all from range"));
1127 selection->set (touched);
1128 commit_reversible_command ();
1132 void
1133 Editor::select_all_selectables_using_punch()
1135 Location* location = _session->locations()->auto_punch_location();
1136 list<Selectable *> touched;
1138 if (location == 0 || (location->end() - location->start() <= 1)) {
1139 return;
1143 TrackViewList* ts;
1145 if (selection->tracks.empty()) {
1146 ts = &track_views;
1147 } else {
1148 ts = &selection->tracks;
1151 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1152 if ((*iter)->hidden()) {
1153 continue;
1155 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1157 begin_reversible_command (_("select all from punch"));
1158 selection->set (touched);
1159 commit_reversible_command ();
1163 void
1164 Editor::select_all_selectables_using_loop()
1166 Location* location = _session->locations()->auto_loop_location();
1167 list<Selectable *> touched;
1169 if (location == 0 || (location->end() - location->start() <= 1)) {
1170 return;
1174 TrackViewList* ts;
1176 if (selection->tracks.empty()) {
1177 ts = &track_views;
1178 } else {
1179 ts = &selection->tracks;
1182 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1183 if ((*iter)->hidden()) {
1184 continue;
1186 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1188 begin_reversible_command (_("select all from loop"));
1189 selection->set (touched);
1190 commit_reversible_command ();
1194 void
1195 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1197 nframes64_t start;
1198 nframes64_t end;
1199 list<Selectable *> touched;
1201 if (after) {
1202 begin_reversible_command (_("select all after cursor"));
1203 start = cursor->current_frame ;
1204 end = _session->current_end_frame();
1205 } else {
1206 if (cursor->current_frame > 0) {
1207 begin_reversible_command (_("select all before cursor"));
1208 start = 0;
1209 end = cursor->current_frame - 1;
1210 } else {
1211 return;
1216 TrackViewList* ts;
1218 if (selection->tracks.empty()) {
1219 ts = &track_views;
1220 } else {
1221 ts = &selection->tracks;
1224 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1225 if ((*iter)->hidden()) {
1226 continue;
1228 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1230 selection->set (touched);
1231 commit_reversible_command ();
1234 void
1235 Editor::select_all_selectables_using_edit (bool after)
1237 nframes64_t start;
1238 nframes64_t end;
1239 list<Selectable *> touched;
1241 if (after) {
1242 begin_reversible_command (_("select all after edit"));
1243 start = get_preferred_edit_position();
1244 end = _session->current_end_frame();
1245 } else {
1246 if ((end = get_preferred_edit_position()) > 1) {
1247 begin_reversible_command (_("select all before edit"));
1248 start = 0;
1249 end -= 1;
1250 } else {
1251 return;
1256 TrackViewList* ts;
1258 if (selection->tracks.empty()) {
1259 ts = &track_views;
1260 } else {
1261 ts = &selection->tracks;
1264 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1265 if ((*iter)->hidden()) {
1266 continue;
1268 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1270 selection->set (touched);
1271 commit_reversible_command ();
1274 void
1275 Editor::select_all_selectables_between (bool /*within*/)
1277 nframes64_t start;
1278 nframes64_t end;
1279 list<Selectable *> touched;
1281 if (!get_edit_op_range (start, end)) {
1282 return;
1285 TrackViewList* ts;
1287 if (selection->tracks.empty()) {
1288 ts = &track_views;
1289 } else {
1290 ts = &selection->tracks;
1293 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1294 if ((*iter)->hidden()) {
1295 continue;
1297 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1300 selection->set (touched);
1303 void
1304 Editor::select_range_between ()
1306 nframes64_t start;
1307 nframes64_t end;
1309 if (!get_edit_op_range (start, end)) {
1310 return;
1313 set_mouse_mode (MouseRange);
1314 selection->set (start, end);
1317 bool
1318 Editor::get_edit_op_range (nframes64_t& start, nframes64_t& end) const
1320 nframes64_t m;
1321 bool ignored;
1323 /* in range mode, use any existing selection */
1325 if (mouse_mode == MouseRange && !selection->time.empty()) {
1326 /* we know that these are ordered */
1327 start = selection->time.start();
1328 end = selection->time.end_frame();
1329 return true;
1332 if (!mouse_frame (m, ignored)) {
1333 /* mouse is not in a canvas, try playhead+selected marker.
1334 this is probably most true when using menus.
1337 if (selection->markers.empty()) {
1338 return false;
1341 start = selection->markers.front()->position();
1342 end = _session->audible_frame();
1344 } else {
1346 switch (_edit_point) {
1347 case EditAtPlayhead:
1348 if (selection->markers.empty()) {
1349 /* use mouse + playhead */
1350 start = m;
1351 end = _session->audible_frame();
1352 } else {
1353 /* use playhead + selected marker */
1354 start = _session->audible_frame();
1355 end = selection->markers.front()->position();
1357 break;
1359 case EditAtMouse:
1360 /* use mouse + selected marker */
1361 if (selection->markers.empty()) {
1362 start = m;
1363 end = _session->audible_frame();
1364 } else {
1365 start = selection->markers.front()->position();
1366 end = m;
1368 break;
1370 case EditAtSelectedMarker:
1371 /* use mouse + selected marker */
1372 if (selection->markers.empty()) {
1374 MessageDialog win (_("No edit range defined"),
1375 false,
1376 MESSAGE_INFO,
1377 BUTTONS_OK);
1379 win.set_secondary_text (
1380 _("the edit point is Selected Marker\nbut there is no selected marker."));
1383 win.set_default_response (RESPONSE_CLOSE);
1384 win.set_position (Gtk::WIN_POS_MOUSE);
1385 win.show_all();
1387 win.run ();
1389 return false; // NO RANGE
1391 start = selection->markers.front()->position();
1392 end = m;
1393 break;
1397 if (start == end) {
1398 return false;
1401 if (start > end) {
1402 swap (start, end);
1405 /* turn range into one delimited by start...end,
1406 not start...end-1
1409 end++;
1411 return true;
1414 void
1415 Editor::deselect_all ()
1417 selection->clear ();
1420 long
1421 Editor::select_range_around_region (RegionView* rv)
1423 assert (rv);
1425 selection->set (&rv->get_time_axis_view());
1427 selection->time.clear ();
1428 boost::shared_ptr<Region> r = rv->region ();
1429 return selection->set (r->position(), r->position() + r->length());