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 if (what_changed.contains (Properties::selected)) {
1009 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1010 (*i)->set_selected (false);
1011 (*i)->hide_selection ();
1015 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1016 * currently selected stripable/controllable duples are found and added
1019 selection->core_selection_changed (what_changed);
1021 /* STEP 2: update TimeAxisView's knowledge of their selected state
1024 if (what_changed.contains (Properties::selected)) {
1026 StripableNotificationListPtr stripables (new StripableNotificationList);
1028 switch (selection->tracks.size()) {
1032 set_selected_mixer_strip (*(selection->tracks.back()));
1033 if (!_track_selection_change_without_scroll) {
1034 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1039 CoreSelection::StripableAutomationControls sc;
1040 _session->selection().get_stripables (sc);
1042 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1044 AxisView* av = axis_view_by_stripable ((*i).stripable);
1050 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1054 continue; /* impossible */
1057 if (!(*i).controllable) {
1059 /* "parent" track selected */
1060 tav->set_selected (true);
1061 tav->reshow_selection (selection->time);
1065 /* possibly a child */
1067 TimeAxisView::Children c = tav->get_child_list ();
1069 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1071 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1073 if (control != (*i).controllable) {
1077 (*j)->set_selected (true);
1078 (*j)->reshow_selection (selection->time);
1082 stripables->push_back ((*i).stripable);
1085 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1087 sensitize_the_right_region_actions (false);
1089 /* STEP 4: notify control protocols */
1091 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1093 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1094 uint32_t audio_track_cnt = 0;
1095 uint32_t midi_track_cnt = 0;
1097 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1098 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1101 if (atv->is_audio_track()) {
1106 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1109 if (mtv->is_midi_track()) {
1116 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1120 /* STEP 4: update EditorRoutes treeview */
1124 soh.add (Properties::selected);
1125 soh.add (Properties::order);
1126 soh.add (Properties::hidden);
1128 if (what_changed.contains (soh)) {
1129 _routes->sync_treeview_from_presentation_info (what_changed);
1134 Editor::time_selection_changed ()
1136 /* XXX this is superficially inefficient. Hide the selection in all
1137 * tracks, then show it in all selected tracks.
1139 * However, if you investigate what this actually does, it isn't
1140 * anywhere nearly as bad as it may appear. Remember: nothing is
1141 * redrawn or even recomputed during these two loops - that only
1142 * happens when we next render ...
1145 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1146 (*i)->hide_selection ();
1149 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1150 (*i)->show_selection (selection->time);
1153 if (selection->time.empty()) {
1154 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1156 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1159 /* propagate into backend, but only when there is no drag or we are at
1160 * the end of a drag, otherwise this is too expensive (could case a
1161 * locate per mouse motion event.
1164 if (_session && !_drags->active()) {
1165 if (selection->time.length() != 0) {
1166 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1168 _session->clear_range_selection ();
1173 /** Set all region actions to have a given sensitivity */
1175 Editor::sensitize_all_region_actions (bool s)
1177 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1179 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1180 (*i)->set_sensitive (s);
1183 _all_region_actions_sensitized = s;
1186 /** Sensitize region-based actions.
1188 * This method is called from whenever we leave the canvas, either by moving
1189 * the pointer out of it, or by popping up a context menu. See
1190 * Editor::{entered,left}_track_canvas() for details there.
1193 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1195 bool have_selection = false;
1196 bool have_entered = false;
1197 bool have_edit_point = false;
1200 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1203 if (!selection->regions.empty()) {
1204 have_selection = true;
1205 rs = selection->regions;
1208 if (entered_regionview) {
1209 have_entered = true;
1210 rs.add (entered_regionview);
1213 if (rs.empty() && !selection->tracks.empty()) {
1215 /* no selected regions, but some selected tracks.
1218 if (_edit_point == EditAtMouse) {
1219 if (!within_track_canvas) {
1220 /* pointer is not in canvas, so edit point is meaningless */
1221 have_edit_point = false;
1223 /* inside canvas. we don't know where the edit
1224 point will be when an action is invoked, but
1225 assume it could intersect with a region.
1227 have_edit_point = true;
1230 RegionSelection at_edit_point;
1231 framepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1232 get_regions_at (at_edit_point, where, selection->tracks);
1233 if (!at_edit_point.empty()) {
1234 have_edit_point = true;
1237 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1242 //std::cerr << "\tfinal have selection: " << have_selection
1243 // << " have entered " << have_entered
1244 // << " have edit point " << have_edit_point
1245 // << " EP = " << enum_2_string (_edit_point)
1248 typedef std::map<std::string,RegionAction> RegionActionMap;
1250 _ignore_region_action = true;
1252 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1253 RegionActionTarget tgt = x->second.target;
1254 bool sensitive = false;
1256 if ((tgt & SelectedRegions) && have_selection) {
1258 } else if ((tgt & EnteredRegions) && have_entered) {
1260 } else if ((tgt & EditPointRegions) && have_edit_point) {
1264 x->second.action->set_sensitive (sensitive);
1267 /* Look through the regions that are selected and make notes about what we have got */
1269 bool have_audio = false;
1270 bool have_multichannel_audio = false;
1271 bool have_midi = false;
1272 bool have_locked = false;
1273 bool have_unlocked = false;
1274 bool have_video_locked = false;
1275 bool have_video_unlocked = false;
1276 bool have_position_lock_style_audio = false;
1277 bool have_position_lock_style_music = false;
1278 bool have_muted = false;
1279 bool have_unmuted = false;
1280 bool have_opaque = false;
1281 bool have_non_opaque = false;
1282 bool have_not_at_natural_position = false;
1283 bool have_envelope_active = false;
1284 bool have_envelope_inactive = false;
1285 bool have_non_unity_scale_amplitude = false;
1286 bool have_compound_regions = false;
1287 bool have_inactive_fade_in = false;
1288 bool have_inactive_fade_out = false;
1289 bool have_active_fade_in = false;
1290 bool have_active_fade_out = false;
1291 bool have_transients = false;
1293 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1295 boost::shared_ptr<Region> r = (*i)->region ();
1296 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1300 if (ar->n_channels() > 1) {
1301 have_multichannel_audio = true;
1305 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1309 if (r->is_compound()) {
1310 have_compound_regions = true;
1316 have_unlocked = true;
1319 if (r->video_locked()) {
1320 have_video_locked = true;
1322 have_video_unlocked = true;
1325 if (r->position_lock_style() == MusicTime) {
1326 have_position_lock_style_music = true;
1328 have_position_lock_style_audio = true;
1334 have_unmuted = true;
1340 have_non_opaque = true;
1343 if (!r->at_natural_position()) {
1344 have_not_at_natural_position = true;
1347 if (r->has_transients ()){
1348 have_transients = true;
1352 if (ar->envelope_active()) {
1353 have_envelope_active = true;
1355 have_envelope_inactive = true;
1358 if (ar->scale_amplitude() != 1) {
1359 have_non_unity_scale_amplitude = true;
1362 if (ar->fade_in_active ()) {
1363 have_active_fade_in = true;
1365 have_inactive_fade_in = true;
1368 if (ar->fade_out_active ()) {
1369 have_active_fade_out = true;
1371 have_inactive_fade_out = true;
1376 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1378 if (rs.size() > 1) {
1379 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1380 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1381 _region_actions->get_action("rename-region")->set_sensitive (false);
1383 /* XXX need to check whether there is than 1 per
1384 playlist, because otherwise this makes no sense.
1386 _region_actions->get_action("combine-regions")->set_sensitive (true);
1388 _region_actions->get_action("combine-regions")->set_sensitive (false);
1390 } else if (rs.size() == 1) {
1391 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1392 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1393 _region_actions->get_action("combine-regions")->set_sensitive (false);
1396 if (!have_multichannel_audio) {
1397 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1401 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1402 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1403 _region_actions->get_action("quantize-region")->set_sensitive (false);
1404 _region_actions->get_action("legatize-region")->set_sensitive (false);
1405 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1406 _region_actions->get_action("transform-region")->set_sensitive (false);
1407 _region_actions->get_action("fork-region")->set_sensitive (false);
1408 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1409 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1410 _region_actions->get_action("transpose-region")->set_sensitive (false);
1412 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1413 /* others were already marked sensitive */
1416 /* ok, moving along... */
1418 if (have_compound_regions) {
1419 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1421 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1426 if (have_envelope_active && !have_envelope_inactive) {
1427 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1428 } else if (have_envelope_active && have_envelope_inactive) {
1429 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1434 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1435 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1436 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1437 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1438 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1439 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1440 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1444 if (!have_non_unity_scale_amplitude || !have_audio) {
1445 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1448 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1449 a->set_active (have_locked && !have_unlocked);
1450 if (have_locked && have_unlocked) {
1451 // a->set_inconsistent ();
1454 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1455 a->set_active (have_video_locked && !have_video_unlocked);
1456 if (have_video_locked && have_video_unlocked) {
1457 // a->set_inconsistent ();
1460 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1461 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1463 vector<Widget*> proxies = a->get_proxies();
1464 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1465 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1467 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1471 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1472 a->set_active (have_muted && !have_unmuted);
1473 if (have_muted && have_unmuted) {
1474 // a->set_inconsistent ();
1477 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1478 a->set_active (have_opaque && !have_non_opaque);
1479 if (have_opaque && have_non_opaque) {
1480 // a->set_inconsistent ();
1483 if (!have_not_at_natural_position) {
1484 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1487 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1488 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1489 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1491 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1494 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1495 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1496 if (have_active_fade_in && have_inactive_fade_in) {
1497 // a->set_inconsistent ();
1500 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1501 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1503 if (have_active_fade_out && have_inactive_fade_out) {
1504 // a->set_inconsistent ();
1507 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1508 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1510 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1511 a->set_active (have_active_fade && !have_inactive_fade);
1513 if (have_active_fade && have_inactive_fade) {
1514 // a->set_inconsistent ();
1517 _ignore_region_action = false;
1519 _all_region_actions_sensitized = false;
1523 Editor::region_selection_changed ()
1525 _regions->block_change_connection (true);
1526 editor_regions_selection_changed_connection.block(true);
1528 if (_region_selection_change_updates_region_list) {
1529 _regions->unselect_all ();
1532 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1533 (*i)->set_selected_regionviews (selection->regions);
1536 if (_region_selection_change_updates_region_list) {
1537 _regions->set_selected (selection->regions);
1540 _regions->block_change_connection (false);
1541 editor_regions_selection_changed_connection.block(false);
1543 sensitize_the_right_region_actions (false);
1545 /* propagate into backend */
1548 if (!selection->regions.empty()) {
1549 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1551 _session->clear_object_selection ();
1558 Editor::point_selection_changed ()
1560 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1561 (*i)->set_selected_points (selection->points);
1566 Editor::select_all_in_track (Selection::Operation op)
1568 list<Selectable *> touched;
1570 if (!clicked_routeview) {
1574 begin_reversible_selection_op (X_("Select All in Track"));
1576 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1579 case Selection::Toggle:
1580 selection->add (touched);
1582 case Selection::Set:
1583 selection->set (touched);
1585 case Selection::Extend:
1586 /* meaningless, because we're selecting everything */
1588 case Selection::Add:
1589 selection->add (touched);
1593 commit_reversible_selection_op ();
1597 Editor::select_all_internal_edit (Selection::Operation)
1599 bool selected = false;
1601 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1602 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1604 mrv->select_all_notes ();
1609 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1611 mrv->select_all_notes ();
1619 Editor::select_all_objects (Selection::Operation op)
1621 list<Selectable *> touched;
1623 if (internal_editing() && select_all_internal_edit(op)) {
1624 return; // Selected notes
1629 if (selection->tracks.empty()) {
1632 ts = selection->tracks;
1635 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1636 if ((*iter)->hidden()) {
1639 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1642 begin_reversible_selection_op (X_("select all"));
1644 case Selection::Add:
1645 selection->add (touched);
1647 case Selection::Toggle:
1648 selection->toggle (touched);
1650 case Selection::Set:
1651 selection->set (touched);
1653 case Selection::Extend:
1654 /* meaningless, because we're selecting everything */
1657 commit_reversible_selection_op ();
1661 Editor::invert_selection_in_track ()
1663 list<Selectable *> touched;
1665 if (!clicked_routeview) {
1669 begin_reversible_selection_op (X_("Invert Selection in Track"));
1670 clicked_routeview->get_inverted_selectables (*selection, touched);
1671 selection->set (touched);
1672 commit_reversible_selection_op ();
1676 Editor::invert_selection ()
1678 list<Selectable *> touched;
1680 if (internal_editing()) {
1681 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1682 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1684 mrv->invert_selection ();
1690 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1691 if ((*iter)->hidden()) {
1694 (*iter)->get_inverted_selectables (*selection, touched);
1697 begin_reversible_selection_op (X_("Invert Selection"));
1698 selection->set (touched);
1699 commit_reversible_selection_op ();
1702 /** @param start Start time in session frames.
1703 * @param end End time in session frames.
1704 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1705 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1706 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1707 * within the region are already selected.
1710 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1712 list<Selectable*> found;
1714 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1716 if ((*iter)->hidden()) {
1720 (*iter)->get_selectables (start, end, top, bot, found);
1723 if (found.empty()) {
1724 selection->clear_objects();
1725 selection->clear_time ();
1729 if (preserve_if_selected && op != Selection::Toggle) {
1730 list<Selectable*>::iterator i = found.begin();
1731 while (i != found.end() && (*i)->selected()) {
1735 if (i == found.end()) {
1740 begin_reversible_selection_op (X_("select all within"));
1742 case Selection::Add:
1743 selection->add (found);
1745 case Selection::Toggle:
1746 selection->toggle (found);
1748 case Selection::Set:
1749 selection->set (found);
1751 case Selection::Extend:
1752 /* not defined yet */
1756 commit_reversible_selection_op ();
1760 Editor::set_selection_from_region ()
1762 if (selection->regions.empty()) {
1766 /* find all the tracks that have selected regions */
1768 set<TimeAxisView*> tracks;
1770 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1771 tracks.insert (&(*r)->get_time_axis_view());
1775 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1777 /* select range (this will clear the region selection) */
1779 selection->set (selection->regions.start(), selection->regions.end_frame());
1781 /* and select the tracks */
1783 selection->set (tvl);
1785 if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1786 set_mouse_mode (Editing::MouseRange, false);
1791 Editor::set_selection_from_punch()
1795 if ((location = _session->locations()->auto_punch_location()) == 0) {
1799 set_selection_from_range (*location);
1803 Editor::set_selection_from_loop()
1807 if ((location = _session->locations()->auto_loop_location()) == 0) {
1810 set_selection_from_range (*location);
1814 Editor::set_selection_from_range (Location& loc)
1816 begin_reversible_selection_op (X_("set selection from range"));
1817 selection->set (loc.start(), loc.end());
1818 commit_reversible_selection_op ();
1820 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1821 set_mouse_mode (MouseRange, false);
1826 Editor::select_all_selectables_using_time_selection ()
1828 list<Selectable *> touched;
1830 if (selection->time.empty()) {
1834 framepos_t start = selection->time[clicked_selection].start;
1835 framepos_t end = selection->time[clicked_selection].end;
1837 if (end - start < 1) {
1843 if (selection->tracks.empty()) {
1846 ts = &selection->tracks;
1849 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1850 if ((*iter)->hidden()) {
1853 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1856 begin_reversible_selection_op (X_("select all from range"));
1857 selection->set (touched);
1858 commit_reversible_selection_op ();
1863 Editor::select_all_selectables_using_punch()
1865 Location* location = _session->locations()->auto_punch_location();
1866 list<Selectable *> touched;
1868 if (location == 0 || (location->end() - location->start() <= 1)) {
1875 if (selection->tracks.empty()) {
1878 ts = &selection->tracks;
1881 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1882 if ((*iter)->hidden()) {
1885 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1887 begin_reversible_selection_op (X_("select all from punch"));
1888 selection->set (touched);
1889 commit_reversible_selection_op ();
1894 Editor::select_all_selectables_using_loop()
1896 Location* location = _session->locations()->auto_loop_location();
1897 list<Selectable *> touched;
1899 if (location == 0 || (location->end() - location->start() <= 1)) {
1906 if (selection->tracks.empty()) {
1909 ts = &selection->tracks;
1912 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1913 if ((*iter)->hidden()) {
1916 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1918 begin_reversible_selection_op (X_("select all from loop"));
1919 selection->set (touched);
1920 commit_reversible_selection_op ();
1925 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1929 list<Selectable *> touched;
1932 start = cursor->current_frame();
1933 end = _session->current_end_frame();
1935 if (cursor->current_frame() > 0) {
1937 end = cursor->current_frame() - 1;
1943 if (internal_editing()) {
1944 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1945 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1947 mrv->select_range (start, end);
1954 begin_reversible_selection_op (X_("select all after cursor"));
1956 begin_reversible_selection_op (X_("select all before cursor"));
1961 if (selection->tracks.empty()) {
1964 ts = &selection->tracks;
1967 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1968 if ((*iter)->hidden()) {
1971 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1973 selection->set (touched);
1974 commit_reversible_selection_op ();
1978 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1982 list<Selectable *> touched;
1985 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1986 end = _session->current_end_frame();
1988 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1996 if (internal_editing()) {
1997 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1998 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1999 mrv->select_range (start, end);
2005 begin_reversible_selection_op (X_("select all after edit"));
2007 begin_reversible_selection_op (X_("select all before edit"));
2012 if (selection->tracks.empty()) {
2015 ts = &selection->tracks;
2018 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2019 if ((*iter)->hidden()) {
2022 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2024 selection->set (touched);
2025 commit_reversible_selection_op ();
2029 Editor::select_all_selectables_between (bool within)
2033 list<Selectable *> touched;
2035 if (!get_edit_op_range (start, end)) {
2039 if (internal_editing()) {
2040 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2041 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2042 mrv->select_range (start, end);
2049 if (selection->tracks.empty()) {
2052 ts = &selection->tracks;
2055 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2056 if ((*iter)->hidden()) {
2059 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2062 begin_reversible_selection_op (X_("Select all Selectables Between"));
2063 selection->set (touched);
2064 commit_reversible_selection_op ();
2068 Editor::select_range_between ()
2073 if ( !selection->time.empty() ) {
2074 selection->clear_time ();
2077 if (!get_edit_op_range (start, end)) {
2081 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2082 set_mouse_mode (MouseRange, false);
2085 begin_reversible_selection_op (X_("Select Range Between"));
2086 selection->set (start, end);
2087 commit_reversible_selection_op ();
2091 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
2096 /* if an explicit range exists, use it */
2098 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
2099 /* we know that these are ordered */
2100 start = selection->time.start();
2101 end = selection->time.end_frame();
2109 // if (!mouse_frame (m, ignored)) {
2110 // /* mouse is not in a canvas, try playhead+selected marker.
2111 // this is probably most true when using menus.
2114 // if (selection->markers.empty()) {
2118 // start = selection->markers.front()->position();
2119 // end = _session->audible_frame();
2123 // switch (_edit_point) {
2124 // case EditAtPlayhead:
2125 // if (selection->markers.empty()) {
2126 // /* use mouse + playhead */
2128 // end = _session->audible_frame();
2130 // /* use playhead + selected marker */
2131 // start = _session->audible_frame();
2132 // end = selection->markers.front()->position();
2136 // case EditAtMouse:
2137 // /* use mouse + selected marker */
2138 // if (selection->markers.empty()) {
2140 // end = _session->audible_frame();
2142 // start = selection->markers.front()->position();
2147 // case EditAtSelectedMarker:
2148 // /* use mouse + selected marker */
2149 // if (selection->markers.empty()) {
2151 // MessageDialog win (_("No edit range defined"),
2156 // win.set_secondary_text (
2157 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2160 // win.set_default_response (RESPONSE_CLOSE);
2161 // win.set_position (Gtk::WIN_POS_MOUSE);
2166 // return false; // NO RANGE
2168 // start = selection->markers.front()->position();
2174 // if (start == end) {
2178 // if (start > end) {
2179 // swap (start, end);
2182 /* turn range into one delimited by start...end,
2192 Editor::deselect_all ()
2194 begin_reversible_selection_op (X_("Deselect All"));
2195 selection->clear ();
2196 commit_reversible_selection_op ();
2200 Editor::select_range (framepos_t s, framepos_t e)
2202 begin_reversible_selection_op (X_("Select Range"));
2203 selection->add (clicked_axisview);
2204 selection->time.clear ();
2205 long ret = selection->set (s, e);
2206 commit_reversible_selection_op ();