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.
23 #include "pbd/stacktrace.h"
24 #include "pbd/unwind.h"
26 #include "ardour/midi_region.h"
27 #include "ardour/playlist.h"
28 #include "ardour/profile.h"
29 #include "ardour/route_group.h"
30 #include "ardour/selection.h"
31 #include "ardour/session.h"
33 #include "control_protocol/control_protocol.h"
36 #include "editor_drag.h"
37 #include "editor_routes.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "audio_streamview.h"
42 #include "automation_line.h"
43 #include "control_point.h"
44 #include "editor_regions.h"
45 #include "editor_cursors.h"
46 #include "midi_region_view.h"
52 using namespace ARDOUR;
56 using namespace Gtkmm2ext;
57 using namespace Editing;
59 struct TrackViewByPositionSorter
61 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
62 return a->y_position() < b->y_position();
67 Editor::extend_selection_to_track (TimeAxisView& view)
69 if (selection->selected (&view)) {
70 /* already selected, do nothing */
74 if (selection->tracks.empty()) {
76 if (!selection->selected (&view)) {
77 selection->set (&view);
84 /* something is already selected, so figure out which range of things to add */
86 TrackViewList to_be_added;
87 TrackViewList sorted = track_views;
88 TrackViewByPositionSorter cmp;
89 bool passed_clicked = false;
94 /* figure out if we should go forward or backwards */
96 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
99 passed_clicked = true;
102 if (selection->selected (*i)) {
103 if (passed_clicked) {
112 passed_clicked = false;
116 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
119 passed_clicked = true;
123 if (passed_clicked) {
124 if ((*i)->hidden()) {
127 if (selection->selected (*i)) {
129 } else if (!(*i)->hidden()) {
130 to_be_added.push_back (*i);
137 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
140 passed_clicked = true;
144 if (passed_clicked) {
146 if ((*r)->hidden()) {
150 if (selection->selected (*r)) {
152 } else if (!(*r)->hidden()) {
153 to_be_added.push_back (*r);
159 if (!selection->selected (&view)) {
160 to_be_added.push_back (&view);
163 if (!to_be_added.empty()) {
164 selection->add (to_be_added);
172 Editor::select_all_tracks ()
174 TrackViewList visible_views;
175 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
176 if ((*i)->marked_for_display()) {
177 visible_views.push_back (*i);
180 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
181 selection->set (visible_views);
184 /** Select clicked_axisview, unless there are no currently selected
185 * tracks, in which case nothing will happen unless `force' is true.
188 Editor::set_selected_track_as_side_effect (Selection::Operation op)
190 if (!clicked_axisview) {
194 RouteGroup* group = NULL;
195 if (clicked_routeview) {
196 group = clicked_routeview->route()->route_group();
200 case Selection::Toggle:
201 if (selection->selected (clicked_axisview)) {
202 if (group && group->is_active()) {
203 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
204 if ((*i)->route_group() == group) {
205 selection->remove(*i);
209 selection->remove (clicked_axisview);
212 if (group && group->is_active()) {
213 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
214 if ((*i)->route_group() == group) {
219 selection->add (clicked_axisview);
225 if (group && group->is_active()) {
226 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
227 if ((*i)->route_group() == group) {
232 selection->add (clicked_axisview);
238 if (group && group->is_active()) {
239 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
240 if ((*i)->route_group() == group) {
245 selection->set (clicked_axisview);
249 case Selection::Extend:
256 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
258 begin_reversible_selection_op (X_("Set Selected Track"));
261 case Selection::Toggle:
262 if (selection->selected (&view)) {
264 selection->remove (&view);
267 selection->add (&view);
272 selection->add (&view);
276 selection->set (&view);
279 case Selection::Extend:
280 extend_selection_to_track (view);
284 commit_reversible_selection_op ();
288 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
290 if (!clicked_routeview) {
298 set_selected_track (*clicked_routeview, op, no_remove);
302 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
304 if (!clicked_control_point) {
312 if (!selection->selected (clicked_control_point)) {
313 selection->set (clicked_control_point);
316 /* clicked on an already selected point */
320 if (selection->points.size() > 1) {
321 selection->set (clicked_control_point);
330 selection->add (clicked_control_point);
334 case Selection::Toggle:
336 /* This is a bit of a hack; if we Primary-Click-Drag a control
337 point (for push drag) we want the point we clicked on to be
338 selected, otherwise we end up confusingly dragging an
339 unselected point. So here we ensure that the point is selected
340 after the press, and if we subsequently get a release (meaning no
341 drag occurred) we set things up so that the toggle has happened.
343 if (press && !selection->selected (clicked_control_point)) {
344 /* This is the button press, and the control point is not selected; make it so,
345 in case this press leads to a drag. Also note that having done this, we don't
346 need to toggle again on release.
348 selection->toggle (clicked_control_point);
349 _control_point_toggled_on_press = true;
351 } else if (!press && !_control_point_toggled_on_press) {
352 /* This is the release, and the point wasn't toggled on the press, so do it now */
353 selection->toggle (clicked_control_point);
357 _control_point_toggled_on_press = false;
360 case Selection::Extend:
369 Editor::get_onscreen_tracks (TrackViewList& tvl)
371 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
372 if ((*i)->y_position() < _visible_canvas_height) {
378 /** Call a slot for a given `basis' track and also for any track that is in the same
379 * active route group with a particular set of properties.
381 * @param sl Slot to call.
382 * @param basis Basis track.
383 * @param prop Properties that active edit groups must share to be included in the map.
387 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
389 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
391 if (route_basis == 0) {
395 set<RouteTimeAxisView*> tracks;
396 tracks.insert (route_basis);
398 RouteGroup* group = route_basis->route()->route_group();
400 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
402 /* the basis is a member of an active route group, with the appropriate
403 properties; find other members */
405 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
406 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
407 if (v && v->route()->route_group() == group) {
414 uint32_t const sz = tracks.size ();
416 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
421 /** Call a slot for a given `basis' track and also for any track that is in the same
422 * active route group with a particular set of properties.
424 * @param sl Slot to call.
425 * @param basis Basis track.
426 * @param prop Properties that active edit groups must share to be included in the map.
430 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
432 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
433 set<boost::shared_ptr<Playlist> > playlists;
435 if (route_basis == 0) {
439 set<RouteTimeAxisView*> tracks;
440 tracks.insert (route_basis);
442 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
444 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
446 /* the basis is a member of an active route group, with the appropriate
447 properties; find other members */
449 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
450 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
452 if (v && v->route()->route_group() == group) {
454 boost::shared_ptr<Track> t = v->track();
456 if (playlists.insert (t->playlist()).second) {
457 /* haven't seen this playlist yet */
461 /* not actually a "Track", but a timeaxis view that
462 we should mapover anyway.
471 uint32_t const sz = tracks.size ();
473 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
479 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
481 boost::shared_ptr<Playlist> pl;
482 vector<boost::shared_ptr<Region> > results;
484 boost::shared_ptr<Track> tr;
486 if ((tr = tv.track()) == 0) {
491 if (&tv == &basis->get_time_axis_view()) {
492 /* looking in same track as the original */
496 if ((pl = tr->playlist()) != 0) {
497 pl->get_equivalent_regions (basis->region(), results);
500 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
501 if ((marv = tv.view()->find_view (*ir)) != 0) {
502 all_equivs->push_back (marv);
508 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
510 mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
512 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
514 equivalent_regions.push_back (basis);
518 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
520 RegionSelection equivalent;
522 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
524 vector<RegionView*> eq;
526 mapover_tracks_with_unique_playlists (
527 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
528 &(*i)->get_time_axis_view(), prop);
530 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
541 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
543 int region_count = 0;
545 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
547 RouteTimeAxisView* tatv;
549 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
551 boost::shared_ptr<Playlist> pl;
552 vector<boost::shared_ptr<Region> > results;
554 boost::shared_ptr<Track> tr;
556 if ((tr = tatv->track()) == 0) {
561 if ((pl = (tr->playlist())) != 0) {
562 pl->get_region_list_equivalent_regions (region, results);
565 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
566 if ((marv = tatv->view()->find_view (*ir)) != 0) {
579 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
581 vector<RegionView*> all_equivalent_regions;
584 if (!clicked_regionview || !clicked_routeview) {
589 button_release_can_deselect = false;
592 if (op == Selection::Toggle || op == Selection::Set) {
595 case Selection::Toggle:
596 if (selection->selected (clicked_regionview)) {
599 /* whatever was clicked was selected already; do nothing here but allow
600 the button release to deselect it
603 button_release_can_deselect = true;
606 if (button_release_can_deselect) {
608 /* just remove this one region, but only on a permitted button release */
610 selection->remove (clicked_regionview);
613 /* no more deselect action on button release till a new press
614 finds an already selected object.
617 button_release_can_deselect = false;
625 if (selection->selected (clicked_routeview)) {
626 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
628 all_equivalent_regions.push_back (clicked_regionview);
631 /* add all the equivalent regions, but only on button press */
633 if (!all_equivalent_regions.empty()) {
637 selection->add (all_equivalent_regions);
643 if (!selection->selected (clicked_regionview)) {
644 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
645 selection->set (all_equivalent_regions);
648 /* clicked on an already selected region */
652 if (selection->regions.size() > 1) {
653 /* collapse region selection down to just this one region (and its equivalents) */
654 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
655 selection->set(all_equivalent_regions);
667 } else if (op == Selection::Extend) {
669 list<Selectable*> results;
670 framepos_t last_frame;
671 framepos_t first_frame;
672 bool same_track = false;
674 /* 1. find the last selected regionview in the track that was clicked in */
677 first_frame = max_framepos;
679 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
680 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
682 if ((*x)->region()->last_frame() > last_frame) {
683 last_frame = (*x)->region()->last_frame();
686 if ((*x)->region()->first_frame() < first_frame) {
687 first_frame = (*x)->region()->first_frame();
696 /* 2. figure out the boundaries for our search for new objects */
698 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
699 case Evoral::OverlapNone:
700 if (last_frame < clicked_regionview->region()->first_frame()) {
701 first_frame = last_frame;
702 last_frame = clicked_regionview->region()->last_frame();
704 last_frame = first_frame;
705 first_frame = clicked_regionview->region()->first_frame();
709 case Evoral::OverlapExternal:
710 if (last_frame < clicked_regionview->region()->first_frame()) {
711 first_frame = last_frame;
712 last_frame = clicked_regionview->region()->last_frame();
714 last_frame = first_frame;
715 first_frame = clicked_regionview->region()->first_frame();
719 case Evoral::OverlapInternal:
720 if (last_frame < clicked_regionview->region()->first_frame()) {
721 first_frame = last_frame;
722 last_frame = clicked_regionview->region()->last_frame();
724 last_frame = first_frame;
725 first_frame = clicked_regionview->region()->first_frame();
729 case Evoral::OverlapStart:
730 case Evoral::OverlapEnd:
731 /* nothing to do except add clicked region to selection, since it
732 overlaps with the existing selection in this track.
739 /* click in a track that has no regions selected, so extend vertically
740 to pick out all regions that are defined by the existing selection
745 first_frame = clicked_regionview->region()->position();
746 last_frame = clicked_regionview->region()->last_frame();
748 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
749 if ((*i)->region()->position() < first_frame) {
750 first_frame = (*i)->region()->position();
752 if ((*i)->region()->last_frame() + 1 > last_frame) {
753 last_frame = (*i)->region()->last_frame();
758 /* 2. find all the tracks we should select in */
760 set<RouteTimeAxisView*> relevant_tracks;
762 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
763 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
765 relevant_tracks.insert (r);
769 set<RouteTimeAxisView*> already_in_selection;
771 if (relevant_tracks.empty()) {
773 /* no tracks selected .. thus .. if the
774 regionview we're in isn't selected
775 (i.e. we're about to extend to it), then
776 find all tracks between the this one and
780 if (!selection->selected (clicked_regionview)) {
782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
786 /* add this track to the ones we will search */
788 relevant_tracks.insert (rtv);
790 /* find the track closest to this one that
791 already a selected region.
794 RouteTimeAxisView* closest = 0;
795 int distance = INT_MAX;
796 int key = rtv->route()->presentation_info().order ();
798 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
800 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
802 if (artv && artv != rtv) {
804 pair<set<RouteTimeAxisView*>::iterator,bool> result;
806 result = already_in_selection.insert (artv);
809 /* newly added to already_in_selection */
811 int d = artv->route()->presentation_info().order ();
815 if (abs (d) < distance) {
825 /* now add all tracks between that one and this one */
827 int okey = closest->route()->presentation_info().order ();
833 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
834 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
835 if (artv && artv != rtv) {
837 int k = artv->route()->presentation_info().order ();
839 if (k >= okey && k <= key) {
841 /* in range but don't add it if
842 it already has tracks selected.
843 this avoids odd selection
844 behaviour that feels wrong.
847 if (find (already_in_selection.begin(),
848 already_in_selection.end(),
849 artv) == already_in_selection.end()) {
851 relevant_tracks.insert (artv);
861 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
862 one that was clicked.
865 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
866 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
869 /* 4. convert to a vector of regions */
871 vector<RegionView*> regions;
873 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
876 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
877 regions.push_back (arv);
881 if (!regions.empty()) {
882 selection->add (regions);
884 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
885 /* ensure that at least the clicked regionview is selected. */
886 selection->set (clicked_regionview);
897 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
902 begin_reversible_selection_op (X_("set selection"));
904 case Selection::Toggle:
905 selection->toggle (s);
910 case Selection::Extend:
918 commit_reversible_selection_op () ;
922 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
924 vector<RegionView*> all_equivalent_regions;
926 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
928 if (all_equivalent_regions.empty()) {
932 begin_reversible_selection_op (X_("set selected regions"));
935 case Selection::Toggle:
936 /* XXX this is not correct */
937 selection->toggle (all_equivalent_regions);
940 selection->set (all_equivalent_regions);
942 case Selection::Extend:
943 selection->add (all_equivalent_regions);
946 selection->add (all_equivalent_regions);
950 commit_reversible_selection_op () ;
954 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
957 boost::shared_ptr<Region> r (weak_r.lock());
963 if ((rv = sv->find_view (r)) == 0) {
967 /* don't reset the selection if its something other than
968 a single other region.
971 if (selection->regions.size() > 1) {
975 begin_reversible_selection_op (X_("set selected regions"));
979 commit_reversible_selection_op () ;
984 struct SelectionOrderSorter {
985 bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const {
986 boost::shared_ptr<Stripable> sa = a->stripable ();
987 boost::shared_ptr<Stripable> sb = b->stripable ();
997 return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
1002 Editor::presentation_info_changed (PropertyChange const & what_changed)
1004 /* We cannot ensure ordering of the handlers for
1005 * PresentationInfo::Changed, so we have to do everything in order
1006 * here, as a single handler.
1009 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1010 (*i)->set_selected (false);
1011 (*i)->hide_selection ();
1014 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1015 * currently selected stripable/controllable duples are found and added
1018 selection->core_selection_changed (what_changed);
1020 /* STEP 2: update TimeAxisView's knowledge of their selected state
1025 pc.add (Properties::selected);
1027 if (what_changed.contains (Properties::selected)) {
1029 StripableNotificationListPtr stripables (new StripableNotificationList);
1031 switch (selection->tracks.size()) {
1035 set_selected_mixer_strip (*(selection->tracks.back()));
1036 if (!_track_selection_change_without_scroll) {
1037 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1042 CoreSelection::StripableAutomationControls sc;
1043 _session->selection().get_stripables (sc);
1045 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1047 AxisView* av = axis_view_by_stripable ((*i).stripable);
1053 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1056 continue; /* impossible */
1059 if (!(*i).controllable) {
1061 /* "parent" track selected */
1062 tav->set_selected (true);
1063 tav->reshow_selection (selection->time);
1067 /* possibly a child */
1069 TimeAxisView::Children c = tav->get_child_list ();
1071 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1073 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1075 if (control != (*i).controllable) {
1079 (*j)->set_selected (true);
1080 (*j)->reshow_selection (selection->time);
1084 stripables->push_back ((*i).stripable);
1087 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1089 sensitize_the_right_region_actions (false);
1091 /* STEP 4: notify control protocols */
1093 ControlProtocol::StripableSelectionChanged (stripables);
1095 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1096 uint32_t audio_track_cnt = 0;
1097 uint32_t midi_track_cnt = 0;
1099 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1100 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1103 if (atv->is_audio_track()) {
1108 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1111 if (mtv->is_midi_track()) {
1118 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1122 /* STEP 4: update EditorRoutes treeview */
1126 soh.add (Properties::selected);
1127 soh.add (Properties::order);
1128 soh.add (Properties::hidden);
1130 if (what_changed.contains (soh)) {
1131 _routes->sync_treeview_from_presentation_info (what_changed);
1136 Editor::time_selection_changed ()
1138 /* XXX this is superficially inefficient. Hide the selection in all
1139 * tracks, then show it in all selected tracks.
1141 * However, if you investigate what this actually does, it isn't
1142 * anywhere nearly as bad as it may appear. Remember: nothing is
1143 * redrawn or even recomputed during these two loops - that only
1144 * happens when we next render ...
1147 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1148 (*i)->hide_selection ();
1151 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1152 (*i)->show_selection (selection->time);
1155 if (selection->time.empty()) {
1156 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1158 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1161 /* propagate into backend, but only when there is no drag or we are at
1162 * the end of a drag, otherwise this is too expensive (could case a
1163 * locate per mouse motion event.
1166 if (_session && !_drags->active()) {
1167 if (selection->time.length() != 0) {
1168 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1170 _session->clear_range_selection ();
1175 /** Set all region actions to have a given sensitivity */
1177 Editor::sensitize_all_region_actions (bool s)
1179 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1181 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1182 (*i)->set_sensitive (s);
1185 _all_region_actions_sensitized = s;
1188 /** Sensitize region-based actions.
1190 * This method is called from whenever we leave the canvas, either by moving
1191 * the pointer out of it, or by popping up a context menu. See
1192 * Editor::{entered,left}_track_canvas() for details there.
1195 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1197 bool have_selection = false;
1198 bool have_entered = false;
1199 bool have_edit_point = false;
1202 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1205 if (!selection->regions.empty()) {
1206 have_selection = true;
1207 rs = selection->regions;
1210 if (entered_regionview) {
1211 have_entered = true;
1212 rs.add (entered_regionview);
1215 if (rs.empty() && !selection->tracks.empty()) {
1217 /* no selected regions, but some selected tracks.
1220 if (_edit_point == EditAtMouse) {
1221 if (!within_track_canvas) {
1222 /* pointer is not in canvas, so edit point is meaningless */
1223 have_edit_point = false;
1225 /* inside canvas. we don't know where the edit
1226 point will be when an action is invoked, but
1227 assume it could intersect with a region.
1229 have_edit_point = true;
1232 RegionSelection at_edit_point;
1233 framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1234 get_regions_at (at_edit_point, where, selection->tracks);
1235 if (!at_edit_point.empty()) {
1236 have_edit_point = true;
1239 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1244 //std::cerr << "\tfinal have selection: " << have_selection
1245 // << " have entered " << have_entered
1246 // << " have edit point " << have_edit_point
1247 // << " EP = " << enum_2_string (_edit_point)
1250 typedef std::map<std::string,RegionAction> RegionActionMap;
1252 _ignore_region_action = true;
1254 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1255 RegionActionTarget tgt = x->second.target;
1256 bool sensitive = false;
1258 if ((tgt & SelectedRegions) && have_selection) {
1260 } else if ((tgt & EnteredRegions) && have_entered) {
1262 } else if ((tgt & EditPointRegions) && have_edit_point) {
1266 x->second.action->set_sensitive (sensitive);
1269 /* Look through the regions that are selected and make notes about what we have got */
1271 bool have_audio = false;
1272 bool have_multichannel_audio = false;
1273 bool have_midi = false;
1274 bool have_locked = false;
1275 bool have_unlocked = false;
1276 bool have_video_locked = false;
1277 bool have_video_unlocked = false;
1278 bool have_position_lock_style_audio = false;
1279 bool have_position_lock_style_music = false;
1280 bool have_muted = false;
1281 bool have_unmuted = false;
1282 bool have_opaque = false;
1283 bool have_non_opaque = false;
1284 bool have_not_at_natural_position = false;
1285 bool have_envelope_active = false;
1286 bool have_envelope_inactive = false;
1287 bool have_non_unity_scale_amplitude = false;
1288 bool have_compound_regions = false;
1289 bool have_inactive_fade_in = false;
1290 bool have_inactive_fade_out = false;
1291 bool have_active_fade_in = false;
1292 bool have_active_fade_out = false;
1293 bool have_transients = false;
1295 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1297 boost::shared_ptr<Region> r = (*i)->region ();
1298 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1302 if (ar->n_channels() > 1) {
1303 have_multichannel_audio = true;
1307 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1311 if (r->is_compound()) {
1312 have_compound_regions = true;
1318 have_unlocked = true;
1321 if (r->video_locked()) {
1322 have_video_locked = true;
1324 have_video_unlocked = true;
1327 if (r->position_lock_style() == MusicTime) {
1328 have_position_lock_style_music = true;
1330 have_position_lock_style_audio = true;
1336 have_unmuted = true;
1342 have_non_opaque = true;
1345 if (!r->at_natural_position()) {
1346 have_not_at_natural_position = true;
1349 if (r->has_transients ()){
1350 have_transients = true;
1354 if (ar->envelope_active()) {
1355 have_envelope_active = true;
1357 have_envelope_inactive = true;
1360 if (ar->scale_amplitude() != 1) {
1361 have_non_unity_scale_amplitude = true;
1364 if (ar->fade_in_active ()) {
1365 have_active_fade_in = true;
1367 have_inactive_fade_in = true;
1370 if (ar->fade_out_active ()) {
1371 have_active_fade_out = true;
1373 have_inactive_fade_out = true;
1378 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1380 if (rs.size() > 1) {
1381 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1382 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1383 _region_actions->get_action("rename-region")->set_sensitive (false);
1385 /* XXX need to check whether there is than 1 per
1386 playlist, because otherwise this makes no sense.
1388 _region_actions->get_action("combine-regions")->set_sensitive (true);
1390 _region_actions->get_action("combine-regions")->set_sensitive (false);
1392 } else if (rs.size() == 1) {
1393 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1394 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1395 _region_actions->get_action("combine-regions")->set_sensitive (false);
1398 if (!have_multichannel_audio) {
1399 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1403 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1404 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1405 _region_actions->get_action("quantize-region")->set_sensitive (false);
1406 _region_actions->get_action("legatize-region")->set_sensitive (false);
1407 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1408 _region_actions->get_action("transform-region")->set_sensitive (false);
1409 _region_actions->get_action("fork-region")->set_sensitive (false);
1410 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1411 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1412 _region_actions->get_action("transpose-region")->set_sensitive (false);
1414 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1415 /* others were already marked sensitive */
1418 /* ok, moving along... */
1420 if (have_compound_regions) {
1421 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1423 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1428 if (have_envelope_active && !have_envelope_inactive) {
1429 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1430 } else if (have_envelope_active && have_envelope_inactive) {
1431 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1436 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1437 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1438 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1439 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1440 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1441 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1442 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1446 if (!have_non_unity_scale_amplitude || !have_audio) {
1447 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1450 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1451 a->set_active (have_locked && !have_unlocked);
1452 if (have_locked && have_unlocked) {
1453 // a->set_inconsistent ();
1456 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1457 a->set_active (have_video_locked && !have_video_unlocked);
1458 if (have_video_locked && have_video_unlocked) {
1459 // a->set_inconsistent ();
1462 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1463 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1465 vector<Widget*> proxies = a->get_proxies();
1466 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1467 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1469 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1473 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1474 a->set_active (have_muted && !have_unmuted);
1475 if (have_muted && have_unmuted) {
1476 // a->set_inconsistent ();
1479 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1480 a->set_active (have_opaque && !have_non_opaque);
1481 if (have_opaque && have_non_opaque) {
1482 // a->set_inconsistent ();
1485 if (!have_not_at_natural_position) {
1486 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1489 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1490 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1491 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1493 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1496 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1497 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1498 if (have_active_fade_in && have_inactive_fade_in) {
1499 // a->set_inconsistent ();
1502 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1503 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1505 if (have_active_fade_out && have_inactive_fade_out) {
1506 // a->set_inconsistent ();
1509 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1510 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1512 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1513 a->set_active (have_active_fade && !have_inactive_fade);
1515 if (have_active_fade && have_inactive_fade) {
1516 // a->set_inconsistent ();
1519 _ignore_region_action = false;
1521 _all_region_actions_sensitized = false;
1525 Editor::region_selection_changed ()
1527 _regions->block_change_connection (true);
1528 editor_regions_selection_changed_connection.block(true);
1530 if (_region_selection_change_updates_region_list) {
1531 _regions->unselect_all ();
1534 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1535 (*i)->set_selected_regionviews (selection->regions);
1538 if (_region_selection_change_updates_region_list) {
1539 _regions->set_selected (selection->regions);
1542 _regions->block_change_connection (false);
1543 editor_regions_selection_changed_connection.block(false);
1545 sensitize_the_right_region_actions (false);
1547 /* propagate into backend */
1550 if (!selection->regions.empty()) {
1551 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1553 _session->clear_object_selection ();
1560 Editor::point_selection_changed ()
1562 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1563 (*i)->set_selected_points (selection->points);
1568 Editor::select_all_in_track (Selection::Operation op)
1570 list<Selectable *> touched;
1572 if (!clicked_routeview) {
1576 begin_reversible_selection_op (X_("Select All in Track"));
1578 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1581 case Selection::Toggle:
1582 selection->add (touched);
1584 case Selection::Set:
1585 selection->set (touched);
1587 case Selection::Extend:
1588 /* meaningless, because we're selecting everything */
1590 case Selection::Add:
1591 selection->add (touched);
1595 commit_reversible_selection_op ();
1599 Editor::select_all_internal_edit (Selection::Operation)
1601 bool selected = false;
1603 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1604 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1606 mrv->select_all_notes ();
1611 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1613 mrv->select_all_notes ();
1621 Editor::select_all_objects (Selection::Operation op)
1623 list<Selectable *> touched;
1625 if (internal_editing() && select_all_internal_edit(op)) {
1626 return; // Selected notes
1631 if (selection->tracks.empty()) {
1634 ts = selection->tracks;
1637 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1638 if ((*iter)->hidden()) {
1641 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1644 begin_reversible_selection_op (X_("select all"));
1646 case Selection::Add:
1647 selection->add (touched);
1649 case Selection::Toggle:
1650 selection->toggle (touched);
1652 case Selection::Set:
1653 selection->set (touched);
1655 case Selection::Extend:
1656 /* meaningless, because we're selecting everything */
1659 commit_reversible_selection_op ();
1663 Editor::invert_selection_in_track ()
1665 list<Selectable *> touched;
1667 if (!clicked_routeview) {
1671 begin_reversible_selection_op (X_("Invert Selection in Track"));
1672 clicked_routeview->get_inverted_selectables (*selection, touched);
1673 selection->set (touched);
1674 commit_reversible_selection_op ();
1678 Editor::invert_selection ()
1680 list<Selectable *> touched;
1682 if (internal_editing()) {
1683 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1684 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1686 mrv->invert_selection ();
1692 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1693 if ((*iter)->hidden()) {
1696 (*iter)->get_inverted_selectables (*selection, touched);
1699 begin_reversible_selection_op (X_("Invert Selection"));
1700 selection->set (touched);
1701 commit_reversible_selection_op ();
1704 /** @param start Start time in session frames.
1705 * @param end End time in session frames.
1706 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1707 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1708 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1709 * within the region are already selected.
1712 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1714 list<Selectable*> found;
1716 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1718 if ((*iter)->hidden()) {
1722 (*iter)->get_selectables (start, end, top, bot, found);
1725 if (found.empty()) {
1726 selection->clear_objects();
1727 selection->clear_time ();
1731 if (preserve_if_selected && op != Selection::Toggle) {
1732 list<Selectable*>::iterator i = found.begin();
1733 while (i != found.end() && (*i)->selected()) {
1737 if (i == found.end()) {
1742 begin_reversible_selection_op (X_("select all within"));
1744 case Selection::Add:
1745 selection->add (found);
1747 case Selection::Toggle:
1748 selection->toggle (found);
1750 case Selection::Set:
1751 selection->set (found);
1753 case Selection::Extend:
1754 /* not defined yet */
1758 commit_reversible_selection_op ();
1762 Editor::set_selection_from_region ()
1764 if (selection->regions.empty()) {
1768 /* find all the tracks that have selected regions */
1770 set<TimeAxisView*> tracks;
1772 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1773 tracks.insert (&(*r)->get_time_axis_view());
1777 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1779 /* select range (this will clear the region selection) */
1781 selection->set (selection->regions.start(), selection->regions.end_frame());
1783 /* and select the tracks */
1785 selection->set (tvl);
1787 if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1788 set_mouse_mode (Editing::MouseRange, false);
1793 Editor::set_selection_from_punch()
1797 if ((location = _session->locations()->auto_punch_location()) == 0) {
1801 set_selection_from_range (*location);
1805 Editor::set_selection_from_loop()
1809 if ((location = _session->locations()->auto_loop_location()) == 0) {
1812 set_selection_from_range (*location);
1816 Editor::set_selection_from_range (Location& loc)
1818 begin_reversible_selection_op (X_("set selection from range"));
1819 selection->set (loc.start(), loc.end());
1820 commit_reversible_selection_op ();
1822 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1823 set_mouse_mode (MouseRange, false);
1828 Editor::select_all_selectables_using_time_selection ()
1830 list<Selectable *> touched;
1832 if (selection->time.empty()) {
1836 framepos_t start = selection->time[clicked_selection].start;
1837 framepos_t end = selection->time[clicked_selection].end;
1839 if (end - start < 1) {
1845 if (selection->tracks.empty()) {
1848 ts = &selection->tracks;
1851 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1852 if ((*iter)->hidden()) {
1855 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1858 begin_reversible_selection_op (X_("select all from range"));
1859 selection->set (touched);
1860 commit_reversible_selection_op ();
1865 Editor::select_all_selectables_using_punch()
1867 Location* location = _session->locations()->auto_punch_location();
1868 list<Selectable *> touched;
1870 if (location == 0 || (location->end() - location->start() <= 1)) {
1877 if (selection->tracks.empty()) {
1880 ts = &selection->tracks;
1883 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1884 if ((*iter)->hidden()) {
1887 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1889 begin_reversible_selection_op (X_("select all from punch"));
1890 selection->set (touched);
1891 commit_reversible_selection_op ();
1896 Editor::select_all_selectables_using_loop()
1898 Location* location = _session->locations()->auto_loop_location();
1899 list<Selectable *> touched;
1901 if (location == 0 || (location->end() - location->start() <= 1)) {
1908 if (selection->tracks.empty()) {
1911 ts = &selection->tracks;
1914 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1915 if ((*iter)->hidden()) {
1918 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1920 begin_reversible_selection_op (X_("select all from loop"));
1921 selection->set (touched);
1922 commit_reversible_selection_op ();
1927 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1931 list<Selectable *> touched;
1934 start = cursor->current_frame();
1935 end = _session->current_end_frame();
1937 if (cursor->current_frame() > 0) {
1939 end = cursor->current_frame() - 1;
1945 if (internal_editing()) {
1946 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1947 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1949 mrv->select_range (start, end);
1956 begin_reversible_selection_op (X_("select all after cursor"));
1958 begin_reversible_selection_op (X_("select all before cursor"));
1963 if (selection->tracks.empty()) {
1966 ts = &selection->tracks;
1969 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1970 if ((*iter)->hidden()) {
1973 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1975 selection->set (touched);
1976 commit_reversible_selection_op ();
1980 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1984 list<Selectable *> touched;
1987 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1988 end = _session->current_end_frame();
1990 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1998 if (internal_editing()) {
1999 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2000 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2001 mrv->select_range (start, end);
2007 begin_reversible_selection_op (X_("select all after edit"));
2009 begin_reversible_selection_op (X_("select all before edit"));
2014 if (selection->tracks.empty()) {
2017 ts = &selection->tracks;
2020 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2021 if ((*iter)->hidden()) {
2024 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2026 selection->set (touched);
2027 commit_reversible_selection_op ();
2031 Editor::select_all_selectables_between (bool within)
2035 list<Selectable *> touched;
2037 if (!get_edit_op_range (start, end)) {
2041 if (internal_editing()) {
2042 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2043 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2044 mrv->select_range (start, end);
2051 if (selection->tracks.empty()) {
2054 ts = &selection->tracks;
2057 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2058 if ((*iter)->hidden()) {
2061 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2064 begin_reversible_selection_op (X_("Select all Selectables Between"));
2065 selection->set (touched);
2066 commit_reversible_selection_op ();
2070 Editor::select_range_between ()
2075 if ( !selection->time.empty() ) {
2076 selection->clear_time ();
2079 if (!get_edit_op_range (start, end)) {
2083 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2084 set_mouse_mode (MouseRange, false);
2087 begin_reversible_selection_op (X_("Select Range Between"));
2088 selection->set (start, end);
2089 commit_reversible_selection_op ();
2093 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
2098 /* if an explicit range exists, use it */
2100 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
2101 /* we know that these are ordered */
2102 start = selection->time.start();
2103 end = selection->time.end_frame();
2111 // if (!mouse_frame (m, ignored)) {
2112 // /* mouse is not in a canvas, try playhead+selected marker.
2113 // this is probably most true when using menus.
2116 // if (selection->markers.empty()) {
2120 // start = selection->markers.front()->position();
2121 // end = _session->audible_frame();
2125 // switch (_edit_point) {
2126 // case EditAtPlayhead:
2127 // if (selection->markers.empty()) {
2128 // /* use mouse + playhead */
2130 // end = _session->audible_frame();
2132 // /* use playhead + selected marker */
2133 // start = _session->audible_frame();
2134 // end = selection->markers.front()->position();
2138 // case EditAtMouse:
2139 // /* use mouse + selected marker */
2140 // if (selection->markers.empty()) {
2142 // end = _session->audible_frame();
2144 // start = selection->markers.front()->position();
2149 // case EditAtSelectedMarker:
2150 // /* use mouse + selected marker */
2151 // if (selection->markers.empty()) {
2153 // MessageDialog win (_("No edit range defined"),
2158 // win.set_secondary_text (
2159 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2162 // win.set_default_response (RESPONSE_CLOSE);
2163 // win.set_position (Gtk::WIN_POS_MOUSE);
2168 // return false; // NO RANGE
2170 // start = selection->markers.front()->position();
2176 // if (start == end) {
2180 // if (start > end) {
2181 // swap (start, end);
2184 /* turn range into one delimited by start...end,
2194 Editor::deselect_all ()
2196 begin_reversible_selection_op (X_("Deselect All"));
2197 selection->clear ();
2198 commit_reversible_selection_op ();
2202 Editor::select_range (framepos_t s, framepos_t e)
2204 begin_reversible_selection_op (X_("Select Range"));
2205 selection->add (clicked_axisview);
2206 selection->time.clear ();
2207 long ret = selection->set (s, e);
2208 commit_reversible_selection_op ();