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/control_protocol_manager.h"
27 #include "ardour/midi_region.h"
28 #include "ardour/playlist.h"
29 #include "ardour/profile.h"
30 #include "ardour/route_group.h"
31 #include "ardour/selection.h"
32 #include "ardour/session.h"
35 #include "editor_drag.h"
36 #include "editor_routes.h"
38 #include "audio_time_axis.h"
39 #include "audio_region_view.h"
40 #include "audio_streamview.h"
41 #include "automation_line.h"
42 #include "control_point.h"
43 #include "editor_regions.h"
44 #include "editor_cursors.h"
45 #include "midi_region_view.h"
51 using namespace ARDOUR;
55 using namespace Gtkmm2ext;
56 using namespace Editing;
58 struct TrackViewByPositionSorter
60 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
61 return a->y_position() < b->y_position();
66 Editor::extend_selection_to_track (TimeAxisView& view)
68 if (selection->selected (&view)) {
69 /* already selected, do nothing */
73 if (selection->tracks.empty()) {
75 if (!selection->selected (&view)) {
76 selection->set (&view);
83 /* something is already selected, so figure out which range of things to add */
85 TrackViewList to_be_added;
86 TrackViewList sorted = track_views;
87 TrackViewByPositionSorter cmp;
88 bool passed_clicked = false;
93 /* figure out if we should go forward or backwards */
95 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
98 passed_clicked = true;
101 if (selection->selected (*i)) {
102 if (passed_clicked) {
111 passed_clicked = false;
115 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
118 passed_clicked = true;
122 if (passed_clicked) {
123 if ((*i)->hidden()) {
126 if (selection->selected (*i)) {
128 } else if (!(*i)->hidden()) {
129 to_be_added.push_back (*i);
136 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
139 passed_clicked = true;
143 if (passed_clicked) {
145 if ((*r)->hidden()) {
149 if (selection->selected (*r)) {
151 } else if (!(*r)->hidden()) {
152 to_be_added.push_back (*r);
158 if (!selection->selected (&view)) {
159 to_be_added.push_back (&view);
162 if (!to_be_added.empty()) {
163 selection->add (to_be_added);
171 Editor::select_all_tracks ()
173 TrackViewList visible_views;
174 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
175 if ((*i)->marked_for_display()) {
176 visible_views.push_back (*i);
179 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
180 selection->set (visible_views);
183 /** Select clicked_axisview, unless there are no currently selected
184 * tracks, in which case nothing will happen unless `force' is true.
187 Editor::set_selected_track_as_side_effect (Selection::Operation op)
189 if (!clicked_axisview) {
193 RouteGroup* group = NULL;
194 if (clicked_routeview) {
195 group = clicked_routeview->route()->route_group();
199 case Selection::Toggle:
200 if (selection->selected (clicked_axisview)) {
201 if (group && group->is_active()) {
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203 if ((*i)->route_group() == group) {
204 selection->remove(*i);
208 selection->remove (clicked_axisview);
211 if (group && group->is_active()) {
212 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
213 if ((*i)->route_group() == group) {
218 selection->add (clicked_axisview);
224 if (group && group->is_active()) {
225 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
226 if ((*i)->route_group() == group) {
231 selection->add (clicked_axisview);
237 if (group && group->is_active()) {
238 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 if ((*i)->route_group() == group) {
244 selection->set (clicked_axisview);
248 case Selection::Extend:
255 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
257 begin_reversible_selection_op (X_("Set Selected Track"));
260 case Selection::Toggle:
261 if (selection->selected (&view)) {
263 selection->remove (&view);
266 selection->add (&view);
271 selection->add (&view);
275 selection->set (&view);
278 case Selection::Extend:
279 extend_selection_to_track (view);
283 commit_reversible_selection_op ();
287 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
289 if (!clicked_routeview) {
297 set_selected_track (*clicked_routeview, op, no_remove);
301 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
303 if (!clicked_control_point) {
311 if (!selection->selected (clicked_control_point)) {
312 selection->set (clicked_control_point);
315 /* clicked on an already selected point */
319 if (selection->points.size() > 1) {
320 selection->set (clicked_control_point);
329 selection->add (clicked_control_point);
333 case Selection::Toggle:
335 /* This is a bit of a hack; if we Primary-Click-Drag a control
336 point (for push drag) we want the point we clicked on to be
337 selected, otherwise we end up confusingly dragging an
338 unselected point. So here we ensure that the point is selected
339 after the press, and if we subsequently get a release (meaning no
340 drag occurred) we set things up so that the toggle has happened.
342 if (press && !selection->selected (clicked_control_point)) {
343 /* This is the button press, and the control point is not selected; make it so,
344 in case this press leads to a drag. Also note that having done this, we don't
345 need to toggle again on release.
347 selection->toggle (clicked_control_point);
348 _control_point_toggled_on_press = true;
350 } else if (!press && !_control_point_toggled_on_press) {
351 /* This is the release, and the point wasn't toggled on the press, so do it now */
352 selection->toggle (clicked_control_point);
356 _control_point_toggled_on_press = false;
359 case Selection::Extend:
368 Editor::get_onscreen_tracks (TrackViewList& tvl)
370 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
371 if ((*i)->y_position() < _visible_canvas_height) {
377 /** Call a slot for a given `basis' track and also for any track that is in the same
378 * active route group with a particular set of properties.
380 * @param sl Slot to call.
381 * @param basis Basis track.
382 * @param prop Properties that active edit groups must share to be included in the map.
386 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
388 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
390 if (route_basis == 0) {
394 set<RouteTimeAxisView*> tracks;
395 tracks.insert (route_basis);
397 RouteGroup* group = route_basis->route()->route_group();
399 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
401 /* the basis is a member of an active route group, with the appropriate
402 properties; find other members */
404 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
405 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
406 if (v && v->route()->route_group() == group) {
413 uint32_t const sz = tracks.size ();
415 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
420 /** Call a slot for a given `basis' track and also for any track that is in the same
421 * active route group with a particular set of properties.
423 * @param sl Slot to call.
424 * @param basis Basis track.
425 * @param prop Properties that active edit groups must share to be included in the map.
429 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
431 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
432 set<boost::shared_ptr<Playlist> > playlists;
434 if (route_basis == 0) {
438 set<RouteTimeAxisView*> tracks;
439 tracks.insert (route_basis);
441 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
443 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
445 /* the basis is a member of an active route group, with the appropriate
446 properties; find other members */
448 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
449 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
451 if (v && v->route()->route_group() == group) {
453 boost::shared_ptr<Track> t = v->track();
455 if (playlists.insert (t->playlist()).second) {
456 /* haven't seen this playlist yet */
460 /* not actually a "Track", but a timeaxis view that
461 we should mapover anyway.
470 uint32_t const sz = tracks.size ();
472 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
478 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
480 boost::shared_ptr<Playlist> pl;
481 vector<boost::shared_ptr<Region> > results;
483 boost::shared_ptr<Track> tr;
485 if ((tr = tv.track()) == 0) {
490 if (&tv == &basis->get_time_axis_view()) {
491 /* looking in same track as the original */
495 if ((pl = tr->playlist()) != 0) {
496 pl->get_equivalent_regions (basis->region(), results);
499 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
500 if ((marv = tv.view()->find_view (*ir)) != 0) {
501 all_equivs->push_back (marv);
507 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
509 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);
511 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
513 equivalent_regions.push_back (basis);
517 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
519 RegionSelection equivalent;
521 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
523 vector<RegionView*> eq;
525 mapover_tracks_with_unique_playlists (
526 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
527 &(*i)->get_time_axis_view(), prop);
529 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
540 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
542 int region_count = 0;
544 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
546 RouteTimeAxisView* tatv;
548 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
550 boost::shared_ptr<Playlist> pl;
551 vector<boost::shared_ptr<Region> > results;
553 boost::shared_ptr<Track> tr;
555 if ((tr = tatv->track()) == 0) {
560 if ((pl = (tr->playlist())) != 0) {
561 pl->get_region_list_equivalent_regions (region, results);
564 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
565 if ((marv = tatv->view()->find_view (*ir)) != 0) {
578 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
580 vector<RegionView*> all_equivalent_regions;
583 if (!clicked_regionview || !clicked_routeview) {
588 button_release_can_deselect = false;
591 if (op == Selection::Toggle || op == Selection::Set) {
594 case Selection::Toggle:
595 if (selection->selected (clicked_regionview)) {
598 /* whatever was clicked was selected already; do nothing here but allow
599 the button release to deselect it
602 button_release_can_deselect = true;
605 if (button_release_can_deselect) {
607 /* just remove this one region, but only on a permitted button release */
609 selection->remove (clicked_regionview);
612 /* no more deselect action on button release till a new press
613 finds an already selected object.
616 button_release_can_deselect = false;
624 if (selection->selected (clicked_routeview)) {
625 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
627 all_equivalent_regions.push_back (clicked_regionview);
630 /* add all the equivalent regions, but only on button press */
632 if (!all_equivalent_regions.empty()) {
636 selection->add (all_equivalent_regions);
642 if (!selection->selected (clicked_regionview)) {
643 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
644 selection->set (all_equivalent_regions);
647 /* clicked on an already selected region */
651 if (selection->regions.size() > 1) {
652 /* collapse region selection down to just this one region (and its equivalents) */
653 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
654 selection->set(all_equivalent_regions);
666 } else if (op == Selection::Extend) {
668 list<Selectable*> results;
669 framepos_t last_frame;
670 framepos_t first_frame;
671 bool same_track = false;
673 /* 1. find the last selected regionview in the track that was clicked in */
676 first_frame = max_framepos;
678 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
679 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
681 if ((*x)->region()->last_frame() > last_frame) {
682 last_frame = (*x)->region()->last_frame();
685 if ((*x)->region()->first_frame() < first_frame) {
686 first_frame = (*x)->region()->first_frame();
695 /* 2. figure out the boundaries for our search for new objects */
697 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
698 case Evoral::OverlapNone:
699 if (last_frame < clicked_regionview->region()->first_frame()) {
700 first_frame = last_frame;
701 last_frame = clicked_regionview->region()->last_frame();
703 last_frame = first_frame;
704 first_frame = clicked_regionview->region()->first_frame();
708 case Evoral::OverlapExternal:
709 if (last_frame < clicked_regionview->region()->first_frame()) {
710 first_frame = last_frame;
711 last_frame = clicked_regionview->region()->last_frame();
713 last_frame = first_frame;
714 first_frame = clicked_regionview->region()->first_frame();
718 case Evoral::OverlapInternal:
719 if (last_frame < clicked_regionview->region()->first_frame()) {
720 first_frame = last_frame;
721 last_frame = clicked_regionview->region()->last_frame();
723 last_frame = first_frame;
724 first_frame = clicked_regionview->region()->first_frame();
728 case Evoral::OverlapStart:
729 case Evoral::OverlapEnd:
730 /* nothing to do except add clicked region to selection, since it
731 overlaps with the existing selection in this track.
738 /* click in a track that has no regions selected, so extend vertically
739 to pick out all regions that are defined by the existing selection
744 first_frame = clicked_regionview->region()->position();
745 last_frame = clicked_regionview->region()->last_frame();
747 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
748 if ((*i)->region()->position() < first_frame) {
749 first_frame = (*i)->region()->position();
751 if ((*i)->region()->last_frame() + 1 > last_frame) {
752 last_frame = (*i)->region()->last_frame();
757 /* 2. find all the tracks we should select in */
759 set<RouteTimeAxisView*> relevant_tracks;
761 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
762 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
764 relevant_tracks.insert (r);
768 set<RouteTimeAxisView*> already_in_selection;
770 if (relevant_tracks.empty()) {
772 /* no tracks selected .. thus .. if the
773 regionview we're in isn't selected
774 (i.e. we're about to extend to it), then
775 find all tracks between the this one and
779 if (!selection->selected (clicked_regionview)) {
781 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
785 /* add this track to the ones we will search */
787 relevant_tracks.insert (rtv);
789 /* find the track closest to this one that
790 already a selected region.
793 RouteTimeAxisView* closest = 0;
794 int distance = INT_MAX;
795 int key = rtv->route()->presentation_info().order ();
797 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
799 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
801 if (artv && artv != rtv) {
803 pair<set<RouteTimeAxisView*>::iterator,bool> result;
805 result = already_in_selection.insert (artv);
808 /* newly added to already_in_selection */
810 int d = artv->route()->presentation_info().order ();
814 if (abs (d) < distance) {
824 /* now add all tracks between that one and this one */
826 int okey = closest->route()->presentation_info().order ();
832 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
833 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
834 if (artv && artv != rtv) {
836 int k = artv->route()->presentation_info().order ();
838 if (k >= okey && k <= key) {
840 /* in range but don't add it if
841 it already has tracks selected.
842 this avoids odd selection
843 behaviour that feels wrong.
846 if (find (already_in_selection.begin(),
847 already_in_selection.end(),
848 artv) == already_in_selection.end()) {
850 relevant_tracks.insert (artv);
860 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
861 one that was clicked.
864 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
865 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
868 /* 4. convert to a vector of regions */
870 vector<RegionView*> regions;
872 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
875 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
876 regions.push_back (arv);
880 if (!regions.empty()) {
881 selection->add (regions);
883 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
884 /* ensure that at least the clicked regionview is selected. */
885 selection->set (clicked_regionview);
896 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
901 begin_reversible_selection_op (X_("set selection"));
903 case Selection::Toggle:
904 selection->toggle (s);
909 case Selection::Extend:
917 commit_reversible_selection_op () ;
921 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
923 vector<RegionView*> all_equivalent_regions;
925 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
927 if (all_equivalent_regions.empty()) {
931 begin_reversible_selection_op (X_("set selected regions"));
934 case Selection::Toggle:
935 /* XXX this is not correct */
936 selection->toggle (all_equivalent_regions);
939 selection->set (all_equivalent_regions);
941 case Selection::Extend:
942 selection->add (all_equivalent_regions);
945 selection->add (all_equivalent_regions);
949 commit_reversible_selection_op () ;
953 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
956 boost::shared_ptr<Region> r (weak_r.lock());
962 if ((rv = sv->find_view (r)) == 0) {
966 /* don't reset the selection if its something other than
967 a single other region.
970 if (selection->regions.size() > 1) {
974 begin_reversible_selection_op (X_("set selected regions"));
978 commit_reversible_selection_op () ;
983 struct SelectionOrderSorter {
984 bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const {
985 boost::shared_ptr<Stripable> sa = a->stripable ();
986 boost::shared_ptr<Stripable> sb = b->stripable ();
996 return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
1001 Editor::presentation_info_changed (PropertyChange const & what_changed)
1003 /* We cannot ensure ordering of the handlers for
1004 * PresentationInfo::Changed, so we have to do everything in order
1005 * here, as a single handler.
1008 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1009 (*i)->set_selected (false);
1010 (*i)->hide_selection ();
1013 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1014 * currently selected stripable/controllable duples are found and added
1017 selection->core_selection_changed (what_changed);
1019 /* STEP 2: update TimeAxisView's knowledge of their selected state
1022 if (what_changed.contains (Properties::selected)) {
1024 StripableNotificationListPtr stripables (new StripableNotificationList);
1026 switch (selection->tracks.size()) {
1030 set_selected_mixer_strip (*(selection->tracks.back()));
1031 if (!_track_selection_change_without_scroll) {
1032 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1037 CoreSelection::StripableAutomationControls sc;
1038 _session->selection().get_stripables (sc);
1040 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1042 AxisView* av = axis_view_by_stripable ((*i).stripable);
1048 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1051 continue; /* impossible */
1054 if (!(*i).controllable) {
1056 /* "parent" track selected */
1057 tav->set_selected (true);
1058 tav->reshow_selection (selection->time);
1062 /* possibly a child */
1064 TimeAxisView::Children c = tav->get_child_list ();
1066 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1068 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1070 if (control != (*i).controllable) {
1074 (*j)->set_selected (true);
1075 (*j)->reshow_selection (selection->time);
1079 stripables->push_back ((*i).stripable);
1082 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1084 sensitize_the_right_region_actions (false);
1086 /* STEP 4: notify control protocols */
1088 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1090 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1091 uint32_t audio_track_cnt = 0;
1092 uint32_t midi_track_cnt = 0;
1094 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1095 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1098 if (atv->is_audio_track()) {
1103 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1106 if (mtv->is_midi_track()) {
1113 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1117 /* STEP 4: update EditorRoutes treeview */
1121 soh.add (Properties::selected);
1122 soh.add (Properties::order);
1123 soh.add (Properties::hidden);
1125 if (what_changed.contains (soh)) {
1126 _routes->sync_treeview_from_presentation_info (what_changed);
1131 Editor::time_selection_changed ()
1133 /* XXX this is superficially inefficient. Hide the selection in all
1134 * tracks, then show it in all selected tracks.
1136 * However, if you investigate what this actually does, it isn't
1137 * anywhere nearly as bad as it may appear. Remember: nothing is
1138 * redrawn or even recomputed during these two loops - that only
1139 * happens when we next render ...
1142 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1143 (*i)->hide_selection ();
1146 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1147 (*i)->show_selection (selection->time);
1150 if (selection->time.empty()) {
1151 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1153 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1156 /* propagate into backend, but only when there is no drag or we are at
1157 * the end of a drag, otherwise this is too expensive (could case a
1158 * locate per mouse motion event.
1161 if (_session && !_drags->active()) {
1162 if (selection->time.length() != 0) {
1163 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1165 _session->clear_range_selection ();
1170 /** Set all region actions to have a given sensitivity */
1172 Editor::sensitize_all_region_actions (bool s)
1174 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1176 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1177 (*i)->set_sensitive (s);
1180 _all_region_actions_sensitized = s;
1183 /** Sensitize region-based actions.
1185 * This method is called from whenever we leave the canvas, either by moving
1186 * the pointer out of it, or by popping up a context menu. See
1187 * Editor::{entered,left}_track_canvas() for details there.
1190 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1192 bool have_selection = false;
1193 bool have_entered = false;
1194 bool have_edit_point = false;
1197 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1200 if (!selection->regions.empty()) {
1201 have_selection = true;
1202 rs = selection->regions;
1205 if (entered_regionview) {
1206 have_entered = true;
1207 rs.add (entered_regionview);
1210 if (rs.empty() && !selection->tracks.empty()) {
1212 /* no selected regions, but some selected tracks.
1215 if (_edit_point == EditAtMouse) {
1216 if (!within_track_canvas) {
1217 /* pointer is not in canvas, so edit point is meaningless */
1218 have_edit_point = false;
1220 /* inside canvas. we don't know where the edit
1221 point will be when an action is invoked, but
1222 assume it could intersect with a region.
1224 have_edit_point = true;
1227 RegionSelection at_edit_point;
1228 framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1229 get_regions_at (at_edit_point, where, selection->tracks);
1230 if (!at_edit_point.empty()) {
1231 have_edit_point = true;
1234 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1239 //std::cerr << "\tfinal have selection: " << have_selection
1240 // << " have entered " << have_entered
1241 // << " have edit point " << have_edit_point
1242 // << " EP = " << enum_2_string (_edit_point)
1245 typedef std::map<std::string,RegionAction> RegionActionMap;
1247 _ignore_region_action = true;
1249 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1250 RegionActionTarget tgt = x->second.target;
1251 bool sensitive = false;
1253 if ((tgt & SelectedRegions) && have_selection) {
1255 } else if ((tgt & EnteredRegions) && have_entered) {
1257 } else if ((tgt & EditPointRegions) && have_edit_point) {
1261 x->second.action->set_sensitive (sensitive);
1264 /* Look through the regions that are selected and make notes about what we have got */
1266 bool have_audio = false;
1267 bool have_multichannel_audio = false;
1268 bool have_midi = false;
1269 bool have_locked = false;
1270 bool have_unlocked = false;
1271 bool have_video_locked = false;
1272 bool have_video_unlocked = false;
1273 bool have_position_lock_style_audio = false;
1274 bool have_position_lock_style_music = false;
1275 bool have_muted = false;
1276 bool have_unmuted = false;
1277 bool have_opaque = false;
1278 bool have_non_opaque = false;
1279 bool have_not_at_natural_position = false;
1280 bool have_envelope_active = false;
1281 bool have_envelope_inactive = false;
1282 bool have_non_unity_scale_amplitude = false;
1283 bool have_compound_regions = false;
1284 bool have_inactive_fade_in = false;
1285 bool have_inactive_fade_out = false;
1286 bool have_active_fade_in = false;
1287 bool have_active_fade_out = false;
1288 bool have_transients = false;
1290 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1292 boost::shared_ptr<Region> r = (*i)->region ();
1293 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1297 if (ar->n_channels() > 1) {
1298 have_multichannel_audio = true;
1302 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1306 if (r->is_compound()) {
1307 have_compound_regions = true;
1313 have_unlocked = true;
1316 if (r->video_locked()) {
1317 have_video_locked = true;
1319 have_video_unlocked = true;
1322 if (r->position_lock_style() == MusicTime) {
1323 have_position_lock_style_music = true;
1325 have_position_lock_style_audio = true;
1331 have_unmuted = true;
1337 have_non_opaque = true;
1340 if (!r->at_natural_position()) {
1341 have_not_at_natural_position = true;
1344 if (r->has_transients ()){
1345 have_transients = true;
1349 if (ar->envelope_active()) {
1350 have_envelope_active = true;
1352 have_envelope_inactive = true;
1355 if (ar->scale_amplitude() != 1) {
1356 have_non_unity_scale_amplitude = true;
1359 if (ar->fade_in_active ()) {
1360 have_active_fade_in = true;
1362 have_inactive_fade_in = true;
1365 if (ar->fade_out_active ()) {
1366 have_active_fade_out = true;
1368 have_inactive_fade_out = true;
1373 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1375 if (rs.size() > 1) {
1376 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1377 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1378 _region_actions->get_action("rename-region")->set_sensitive (false);
1380 /* XXX need to check whether there is than 1 per
1381 playlist, because otherwise this makes no sense.
1383 _region_actions->get_action("combine-regions")->set_sensitive (true);
1385 _region_actions->get_action("combine-regions")->set_sensitive (false);
1387 } else if (rs.size() == 1) {
1388 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1389 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1390 _region_actions->get_action("combine-regions")->set_sensitive (false);
1393 if (!have_multichannel_audio) {
1394 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1398 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1399 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1400 _region_actions->get_action("quantize-region")->set_sensitive (false);
1401 _region_actions->get_action("legatize-region")->set_sensitive (false);
1402 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1403 _region_actions->get_action("transform-region")->set_sensitive (false);
1404 _region_actions->get_action("fork-region")->set_sensitive (false);
1405 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1406 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1407 _region_actions->get_action("transpose-region")->set_sensitive (false);
1409 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1410 /* others were already marked sensitive */
1413 /* ok, moving along... */
1415 if (have_compound_regions) {
1416 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1418 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1423 if (have_envelope_active && !have_envelope_inactive) {
1424 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1425 } else if (have_envelope_active && have_envelope_inactive) {
1426 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1431 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1432 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1433 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1434 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1435 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1436 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1437 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1441 if (!have_non_unity_scale_amplitude || !have_audio) {
1442 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1445 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1446 a->set_active (have_locked && !have_unlocked);
1447 if (have_locked && have_unlocked) {
1448 // a->set_inconsistent ();
1451 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1452 a->set_active (have_video_locked && !have_video_unlocked);
1453 if (have_video_locked && have_video_unlocked) {
1454 // a->set_inconsistent ();
1457 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1458 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1460 vector<Widget*> proxies = a->get_proxies();
1461 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1462 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1464 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1468 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1469 a->set_active (have_muted && !have_unmuted);
1470 if (have_muted && have_unmuted) {
1471 // a->set_inconsistent ();
1474 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1475 a->set_active (have_opaque && !have_non_opaque);
1476 if (have_opaque && have_non_opaque) {
1477 // a->set_inconsistent ();
1480 if (!have_not_at_natural_position) {
1481 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1484 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1485 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1486 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1488 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1491 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1492 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1493 if (have_active_fade_in && have_inactive_fade_in) {
1494 // a->set_inconsistent ();
1497 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1498 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1500 if (have_active_fade_out && have_inactive_fade_out) {
1501 // a->set_inconsistent ();
1504 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1505 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1507 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1508 a->set_active (have_active_fade && !have_inactive_fade);
1510 if (have_active_fade && have_inactive_fade) {
1511 // a->set_inconsistent ();
1514 _ignore_region_action = false;
1516 _all_region_actions_sensitized = false;
1520 Editor::region_selection_changed ()
1522 _regions->block_change_connection (true);
1523 editor_regions_selection_changed_connection.block(true);
1525 if (_region_selection_change_updates_region_list) {
1526 _regions->unselect_all ();
1529 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1530 (*i)->set_selected_regionviews (selection->regions);
1533 if (_region_selection_change_updates_region_list) {
1534 _regions->set_selected (selection->regions);
1537 _regions->block_change_connection (false);
1538 editor_regions_selection_changed_connection.block(false);
1540 sensitize_the_right_region_actions (false);
1542 /* propagate into backend */
1545 if (!selection->regions.empty()) {
1546 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1548 _session->clear_object_selection ();
1555 Editor::point_selection_changed ()
1557 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1558 (*i)->set_selected_points (selection->points);
1563 Editor::select_all_in_track (Selection::Operation op)
1565 list<Selectable *> touched;
1567 if (!clicked_routeview) {
1571 begin_reversible_selection_op (X_("Select All in Track"));
1573 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1576 case Selection::Toggle:
1577 selection->add (touched);
1579 case Selection::Set:
1580 selection->set (touched);
1582 case Selection::Extend:
1583 /* meaningless, because we're selecting everything */
1585 case Selection::Add:
1586 selection->add (touched);
1590 commit_reversible_selection_op ();
1594 Editor::select_all_internal_edit (Selection::Operation)
1596 bool selected = false;
1598 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1599 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1601 mrv->select_all_notes ();
1606 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1608 mrv->select_all_notes ();
1616 Editor::select_all_objects (Selection::Operation op)
1618 list<Selectable *> touched;
1620 if (internal_editing() && select_all_internal_edit(op)) {
1621 return; // Selected notes
1626 if (selection->tracks.empty()) {
1629 ts = selection->tracks;
1632 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1633 if ((*iter)->hidden()) {
1636 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1639 begin_reversible_selection_op (X_("select all"));
1641 case Selection::Add:
1642 selection->add (touched);
1644 case Selection::Toggle:
1645 selection->toggle (touched);
1647 case Selection::Set:
1648 selection->set (touched);
1650 case Selection::Extend:
1651 /* meaningless, because we're selecting everything */
1654 commit_reversible_selection_op ();
1658 Editor::invert_selection_in_track ()
1660 list<Selectable *> touched;
1662 if (!clicked_routeview) {
1666 begin_reversible_selection_op (X_("Invert Selection in Track"));
1667 clicked_routeview->get_inverted_selectables (*selection, touched);
1668 selection->set (touched);
1669 commit_reversible_selection_op ();
1673 Editor::invert_selection ()
1675 list<Selectable *> touched;
1677 if (internal_editing()) {
1678 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1679 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1681 mrv->invert_selection ();
1687 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1688 if ((*iter)->hidden()) {
1691 (*iter)->get_inverted_selectables (*selection, touched);
1694 begin_reversible_selection_op (X_("Invert Selection"));
1695 selection->set (touched);
1696 commit_reversible_selection_op ();
1699 /** @param start Start time in session frames.
1700 * @param end End time in session frames.
1701 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1702 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1703 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1704 * within the region are already selected.
1707 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1709 list<Selectable*> found;
1711 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1713 if ((*iter)->hidden()) {
1717 (*iter)->get_selectables (start, end, top, bot, found);
1720 if (found.empty()) {
1721 selection->clear_objects();
1722 selection->clear_time ();
1726 if (preserve_if_selected && op != Selection::Toggle) {
1727 list<Selectable*>::iterator i = found.begin();
1728 while (i != found.end() && (*i)->selected()) {
1732 if (i == found.end()) {
1737 begin_reversible_selection_op (X_("select all within"));
1739 case Selection::Add:
1740 selection->add (found);
1742 case Selection::Toggle:
1743 selection->toggle (found);
1745 case Selection::Set:
1746 selection->set (found);
1748 case Selection::Extend:
1749 /* not defined yet */
1753 commit_reversible_selection_op ();
1757 Editor::set_selection_from_region ()
1759 if (selection->regions.empty()) {
1763 /* find all the tracks that have selected regions */
1765 set<TimeAxisView*> tracks;
1767 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1768 tracks.insert (&(*r)->get_time_axis_view());
1772 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1774 /* select range (this will clear the region selection) */
1776 selection->set (selection->regions.start(), selection->regions.end_frame());
1778 /* and select the tracks */
1780 selection->set (tvl);
1782 if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1783 set_mouse_mode (Editing::MouseRange, false);
1788 Editor::set_selection_from_punch()
1792 if ((location = _session->locations()->auto_punch_location()) == 0) {
1796 set_selection_from_range (*location);
1800 Editor::set_selection_from_loop()
1804 if ((location = _session->locations()->auto_loop_location()) == 0) {
1807 set_selection_from_range (*location);
1811 Editor::set_selection_from_range (Location& loc)
1813 begin_reversible_selection_op (X_("set selection from range"));
1814 selection->set (loc.start(), loc.end());
1815 commit_reversible_selection_op ();
1817 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1818 set_mouse_mode (MouseRange, false);
1823 Editor::select_all_selectables_using_time_selection ()
1825 list<Selectable *> touched;
1827 if (selection->time.empty()) {
1831 framepos_t start = selection->time[clicked_selection].start;
1832 framepos_t end = selection->time[clicked_selection].end;
1834 if (end - start < 1) {
1840 if (selection->tracks.empty()) {
1843 ts = &selection->tracks;
1846 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1847 if ((*iter)->hidden()) {
1850 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1853 begin_reversible_selection_op (X_("select all from range"));
1854 selection->set (touched);
1855 commit_reversible_selection_op ();
1860 Editor::select_all_selectables_using_punch()
1862 Location* location = _session->locations()->auto_punch_location();
1863 list<Selectable *> touched;
1865 if (location == 0 || (location->end() - location->start() <= 1)) {
1872 if (selection->tracks.empty()) {
1875 ts = &selection->tracks;
1878 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1879 if ((*iter)->hidden()) {
1882 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1884 begin_reversible_selection_op (X_("select all from punch"));
1885 selection->set (touched);
1886 commit_reversible_selection_op ();
1891 Editor::select_all_selectables_using_loop()
1893 Location* location = _session->locations()->auto_loop_location();
1894 list<Selectable *> touched;
1896 if (location == 0 || (location->end() - location->start() <= 1)) {
1903 if (selection->tracks.empty()) {
1906 ts = &selection->tracks;
1909 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1910 if ((*iter)->hidden()) {
1913 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1915 begin_reversible_selection_op (X_("select all from loop"));
1916 selection->set (touched);
1917 commit_reversible_selection_op ();
1922 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1926 list<Selectable *> touched;
1929 start = cursor->current_frame();
1930 end = _session->current_end_frame();
1932 if (cursor->current_frame() > 0) {
1934 end = cursor->current_frame() - 1;
1940 if (internal_editing()) {
1941 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1942 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1944 mrv->select_range (start, end);
1951 begin_reversible_selection_op (X_("select all after cursor"));
1953 begin_reversible_selection_op (X_("select all before cursor"));
1958 if (selection->tracks.empty()) {
1961 ts = &selection->tracks;
1964 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1965 if ((*iter)->hidden()) {
1968 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1970 selection->set (touched);
1971 commit_reversible_selection_op ();
1975 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1979 list<Selectable *> touched;
1982 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1983 end = _session->current_end_frame();
1985 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1993 if (internal_editing()) {
1994 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1995 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1996 mrv->select_range (start, end);
2002 begin_reversible_selection_op (X_("select all after edit"));
2004 begin_reversible_selection_op (X_("select all before edit"));
2009 if (selection->tracks.empty()) {
2012 ts = &selection->tracks;
2015 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2016 if ((*iter)->hidden()) {
2019 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2021 selection->set (touched);
2022 commit_reversible_selection_op ();
2026 Editor::select_all_selectables_between (bool within)
2030 list<Selectable *> touched;
2032 if (!get_edit_op_range (start, end)) {
2036 if (internal_editing()) {
2037 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2038 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2039 mrv->select_range (start, end);
2046 if (selection->tracks.empty()) {
2049 ts = &selection->tracks;
2052 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2053 if ((*iter)->hidden()) {
2056 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2059 begin_reversible_selection_op (X_("Select all Selectables Between"));
2060 selection->set (touched);
2061 commit_reversible_selection_op ();
2065 Editor::select_range_between ()
2070 if ( !selection->time.empty() ) {
2071 selection->clear_time ();
2074 if (!get_edit_op_range (start, end)) {
2078 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2079 set_mouse_mode (MouseRange, false);
2082 begin_reversible_selection_op (X_("Select Range Between"));
2083 selection->set (start, end);
2084 commit_reversible_selection_op ();
2088 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
2093 /* if an explicit range exists, use it */
2095 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
2096 /* we know that these are ordered */
2097 start = selection->time.start();
2098 end = selection->time.end_frame();
2106 // if (!mouse_frame (m, ignored)) {
2107 // /* mouse is not in a canvas, try playhead+selected marker.
2108 // this is probably most true when using menus.
2111 // if (selection->markers.empty()) {
2115 // start = selection->markers.front()->position();
2116 // end = _session->audible_frame();
2120 // switch (_edit_point) {
2121 // case EditAtPlayhead:
2122 // if (selection->markers.empty()) {
2123 // /* use mouse + playhead */
2125 // end = _session->audible_frame();
2127 // /* use playhead + selected marker */
2128 // start = _session->audible_frame();
2129 // end = selection->markers.front()->position();
2133 // case EditAtMouse:
2134 // /* use mouse + selected marker */
2135 // if (selection->markers.empty()) {
2137 // end = _session->audible_frame();
2139 // start = selection->markers.front()->position();
2144 // case EditAtSelectedMarker:
2145 // /* use mouse + selected marker */
2146 // if (selection->markers.empty()) {
2148 // MessageDialog win (_("No edit range defined"),
2153 // win.set_secondary_text (
2154 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2157 // win.set_default_response (RESPONSE_CLOSE);
2158 // win.set_position (Gtk::WIN_POS_MOUSE);
2163 // return false; // NO RANGE
2165 // start = selection->markers.front()->position();
2171 // if (start == end) {
2175 // if (start > end) {
2176 // swap (start, end);
2179 /* turn range into one delimited by start...end,
2189 Editor::deselect_all ()
2191 begin_reversible_selection_op (X_("Deselect All"));
2192 selection->clear ();
2193 commit_reversible_selection_op ();
2197 Editor::select_range (framepos_t s, framepos_t e)
2199 begin_reversible_selection_op (X_("Select Range"));
2200 selection->add (clicked_axisview);
2201 selection->time.clear ();
2202 long ret = selection->set (s, e);
2203 commit_reversible_selection_op ();