2 * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
3 * Copyright (C) 2007-2015 David Robillard <d@drobilla.net>
4 * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5 * Copyright (C) 2013-2017 Nick Mainsbridge <mainsbridge@gmail.com>
6 * Copyright (C) 2013-2019 Robin Gareus <robin@gareus.org>
7 * Copyright (C) 2014-2019 Ben Loftis <ben@harrisonconsoles.com>
8 * Copyright (C) 2015 André Nusser <andre.nusser@googlemail.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include "pbd/stacktrace.h"
29 #include "pbd/unwind.h"
31 #include "ardour/control_protocol_manager.h"
32 #include "ardour/midi_region.h"
33 #include "ardour/playlist.h"
34 #include "ardour/profile.h"
35 #include "ardour/route_group.h"
36 #include "ardour/selection.h"
37 #include "ardour/session.h"
38 #include "ardour/vca.h"
41 #include "editor_drag.h"
42 #include "editor_routes.h"
43 #include "editor_sources.h"
45 #include "audio_time_axis.h"
46 #include "audio_region_view.h"
47 #include "audio_streamview.h"
48 #include "automation_line.h"
49 #include "control_point.h"
50 #include "editor_regions.h"
51 #include "editor_cursors.h"
52 #include "midi_region_view.h"
58 using namespace ARDOUR;
62 using namespace Gtkmm2ext;
63 using namespace Editing;
65 struct TrackViewByPositionSorter
67 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
68 return a->y_position() < b->y_position();
73 Editor::extend_selection_to_track (TimeAxisView& view)
75 if (selection->selected (&view)) {
76 /* already selected, do nothing */
80 if (selection->tracks.empty()) {
82 if (!selection->selected (&view)) {
83 selection->set (&view);
90 /* something is already selected, so figure out which range of things to add */
92 TrackViewList to_be_added;
93 TrackViewList sorted = track_views;
94 TrackViewByPositionSorter cmp;
95 bool passed_clicked = false;
100 /* figure out if we should go forward or backwards */
102 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
105 passed_clicked = true;
108 if (selection->selected (*i)) {
109 if (passed_clicked) {
118 passed_clicked = false;
122 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
125 passed_clicked = true;
129 if (passed_clicked) {
130 if ((*i)->hidden()) {
133 if (selection->selected (*i)) {
135 } else if (!(*i)->hidden()) {
136 to_be_added.push_back (*i);
143 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
146 passed_clicked = true;
150 if (passed_clicked) {
152 if ((*r)->hidden()) {
156 if (selection->selected (*r)) {
158 } else if (!(*r)->hidden()) {
159 to_be_added.push_back (*r);
165 if (!selection->selected (&view)) {
166 to_be_added.push_back (&view);
169 if (!to_be_added.empty()) {
170 selection->add (to_be_added);
178 Editor::select_all_tracks ()
180 TrackViewList visible_views;
181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
182 if ((*i)->marked_for_display()) {
183 visible_views.push_back (*i);
186 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
187 selection->set (visible_views);
190 /** Select clicked_axisview, unless there are no currently selected
191 * tracks, in which case nothing will happen unless `force' is true.
194 Editor::set_selected_track_as_side_effect (Selection::Operation op)
196 if (!clicked_axisview) {
200 PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
202 RouteGroup* group = NULL;
203 if (clicked_routeview) {
204 group = clicked_routeview->route()->route_group();
208 case Selection::Toggle:
209 if (selection->selected (clicked_axisview)) {
210 if (group && group->is_active()) {
211 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
212 if ((*i)->route_group() == group) {
213 selection->remove(*i);
217 selection->remove (clicked_axisview);
220 if (group && group->is_active()) {
221 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
222 if ((*i)->route_group() == group) {
227 selection->add (clicked_axisview);
233 if (group && group->is_active()) {
234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
235 if ((*i)->route_group() == group) {
240 selection->add (clicked_axisview);
246 if (group && group->is_active()) {
247 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
248 if ((*i)->route_group() == group) {
253 selection->set (clicked_axisview);
257 case Selection::Extend:
264 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
266 begin_reversible_selection_op (X_("Set Selected Track"));
269 case Selection::Toggle:
270 if (selection->selected (&view)) {
272 selection->remove (&view);
275 selection->add (&view);
280 selection->add (&view);
284 selection->set (&view);
287 case Selection::Extend:
288 extend_selection_to_track (view);
292 commit_reversible_selection_op ();
296 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
298 if (!clicked_routeview) {
306 set_selected_track (*clicked_routeview, op, no_remove);
310 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
312 if (!clicked_control_point) {
320 if (!selection->selected (clicked_control_point)) {
321 selection->set (clicked_control_point);
324 /* clicked on an already selected point */
328 if (selection->points.size() > 1) {
329 selection->set (clicked_control_point);
338 selection->add (clicked_control_point);
342 case Selection::Toggle:
344 /* This is a bit of a hack; if we Primary-Click-Drag a control
345 point (for push drag) we want the point we clicked on to be
346 selected, otherwise we end up confusingly dragging an
347 unselected point. So here we ensure that the point is selected
348 after the press, and if we subsequently get a release (meaning no
349 drag occurred) we set things up so that the toggle has happened.
351 if (press && !selection->selected (clicked_control_point)) {
352 /* This is the button press, and the control point is not selected; make it so,
353 in case this press leads to a drag. Also note that having done this, we don't
354 need to toggle again on release.
356 selection->toggle (clicked_control_point);
357 _control_point_toggled_on_press = true;
359 } else if (!press && !_control_point_toggled_on_press) {
360 /* This is the release, and the point wasn't toggled on the press, so do it now */
361 selection->toggle (clicked_control_point);
365 _control_point_toggled_on_press = false;
368 case Selection::Extend:
377 Editor::get_onscreen_tracks (TrackViewList& tvl)
379 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
380 if ((*i)->y_position() < _visible_canvas_height) {
386 /** Call a slot for a given `basis' track and also for any track that is in the same
387 * active route group with a particular set of properties.
389 * @param sl Slot to call.
390 * @param basis Basis track.
391 * @param prop Properties that active edit groups must share to be included in the map.
395 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
397 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
399 if (route_basis == 0) {
403 set<RouteTimeAxisView*> tracks;
404 tracks.insert (route_basis);
406 RouteGroup* group = route_basis->route()->route_group();
408 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
410 /* the basis is a member of an active route group, with the appropriate
411 properties; find other members */
413 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
414 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
415 if (v && v->route()->route_group() == group) {
422 uint32_t const sz = tracks.size ();
424 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
429 /** Call a slot for a given `basis' track and also for any track that is in the same
430 * active route group with a particular set of properties.
432 * @param sl Slot to call.
433 * @param basis Basis track.
434 * @param prop Properties that active edit groups must share to be included in the map.
438 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
440 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
441 set<boost::shared_ptr<Playlist> > playlists;
443 if (route_basis == 0) {
447 set<RouteTimeAxisView*> tracks;
448 tracks.insert (route_basis);
450 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
452 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
454 /* the basis is a member of an active route group, with the appropriate
455 properties; find other members */
457 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
458 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
460 if (v && v->route()->route_group() == group) {
462 boost::shared_ptr<Track> t = v->track();
464 if (playlists.insert (t->playlist()).second) {
465 /* haven't seen this playlist yet */
469 /* not actually a "Track", but a timeaxis view that
470 we should mapover anyway.
479 uint32_t const sz = tracks.size ();
481 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
487 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
489 boost::shared_ptr<Playlist> pl;
490 vector<boost::shared_ptr<Region> > results;
492 boost::shared_ptr<Track> tr;
494 if ((tr = tv.track()) == 0) {
499 if (&tv == &basis->get_time_axis_view()) {
500 /* looking in same track as the original */
504 if ((pl = tr->playlist()) != 0) {
505 pl->get_equivalent_regions (basis->region(), results);
508 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
509 if ((marv = tv.view()->find_view (*ir)) != 0) {
510 all_equivs->push_back (marv);
516 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
518 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);
520 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
522 equivalent_regions.push_back (basis);
526 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
528 RegionSelection equivalent;
530 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
532 vector<RegionView*> eq;
534 mapover_tracks_with_unique_playlists (
535 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
536 &(*i)->get_time_axis_view(), prop);
538 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
549 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
551 int region_count = 0;
553 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
555 RouteTimeAxisView* tatv;
557 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
559 boost::shared_ptr<Playlist> pl;
560 vector<boost::shared_ptr<Region> > results;
562 boost::shared_ptr<Track> tr;
564 if ((tr = tatv->track()) == 0) {
569 if ((pl = (tr->playlist())) != 0) {
570 pl->get_region_list_equivalent_regions (region, results);
573 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
574 if ((marv = tatv->view()->find_view (*ir)) != 0) {
587 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
589 vector<RegionView*> all_equivalent_regions;
592 if (!clicked_regionview || !clicked_routeview) {
597 button_release_can_deselect = false;
600 if (op == Selection::Toggle || op == Selection::Set) {
603 case Selection::Toggle:
604 if (selection->selected (clicked_regionview)) {
607 /* whatever was clicked was selected already; do nothing here but allow
608 the button release to deselect it
611 button_release_can_deselect = true;
614 if (button_release_can_deselect) {
616 /* just remove this one region, but only on a permitted button release */
618 selection->remove (clicked_regionview);
621 /* no more deselect action on button release till a new press
622 finds an already selected object.
625 button_release_can_deselect = false;
633 if (selection->selected (clicked_routeview)) {
634 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
636 all_equivalent_regions.push_back (clicked_regionview);
639 /* add all the equivalent regions, but only on button press */
641 if (!all_equivalent_regions.empty()) {
645 selection->add (all_equivalent_regions);
651 if (!selection->selected (clicked_regionview)) {
652 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
653 selection->set (all_equivalent_regions);
656 /* clicked on an already selected region */
660 if (selection->regions.size() > 1) {
661 /* collapse region selection down to just this one region (and its equivalents) */
662 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
663 selection->set(all_equivalent_regions);
675 } else if (op == Selection::Extend) {
677 list<Selectable*> results;
678 samplepos_t last_sample;
679 samplepos_t first_sample;
680 bool same_track = false;
682 /* 1. find the last selected regionview in the track that was clicked in */
685 first_sample = max_samplepos;
687 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
688 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
690 if ((*x)->region()->last_sample() > last_sample) {
691 last_sample = (*x)->region()->last_sample();
694 if ((*x)->region()->first_sample() < first_sample) {
695 first_sample = (*x)->region()->first_sample();
704 /* 2. figure out the boundaries for our search for new objects */
706 switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
707 case Evoral::OverlapNone:
708 if (last_sample < clicked_regionview->region()->first_sample()) {
709 first_sample = last_sample;
710 last_sample = clicked_regionview->region()->last_sample();
712 last_sample = first_sample;
713 first_sample = clicked_regionview->region()->first_sample();
717 case Evoral::OverlapExternal:
718 if (last_sample < clicked_regionview->region()->first_sample()) {
719 first_sample = last_sample;
720 last_sample = clicked_regionview->region()->last_sample();
722 last_sample = first_sample;
723 first_sample = clicked_regionview->region()->first_sample();
727 case Evoral::OverlapInternal:
728 if (last_sample < clicked_regionview->region()->first_sample()) {
729 first_sample = last_sample;
730 last_sample = clicked_regionview->region()->last_sample();
732 last_sample = first_sample;
733 first_sample = clicked_regionview->region()->first_sample();
737 case Evoral::OverlapStart:
738 case Evoral::OverlapEnd:
739 /* nothing to do except add clicked region to selection, since it
740 overlaps with the existing selection in this track.
747 /* click in a track that has no regions selected, so extend vertically
748 to pick out all regions that are defined by the existing selection
753 first_sample = clicked_regionview->region()->position();
754 last_sample = clicked_regionview->region()->last_sample();
756 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
757 if ((*i)->region()->position() < first_sample) {
758 first_sample = (*i)->region()->position();
760 if ((*i)->region()->last_sample() + 1 > last_sample) {
761 last_sample = (*i)->region()->last_sample();
766 /* 2. find all the tracks we should select in */
768 set<RouteTimeAxisView*> relevant_tracks;
770 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
771 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
773 relevant_tracks.insert (r);
777 set<RouteTimeAxisView*> already_in_selection;
779 if (relevant_tracks.empty()) {
781 /* no tracks selected .. thus .. if the
782 regionview we're in isn't selected
783 (i.e. we're about to extend to it), then
784 find all tracks between the this one and
788 if (!selection->selected (clicked_regionview)) {
790 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
794 /* add this track to the ones we will search */
796 relevant_tracks.insert (rtv);
798 /* find the track closest to this one that
799 already a selected region.
802 RouteTimeAxisView* closest = 0;
803 int distance = INT_MAX;
804 int key = rtv->route()->presentation_info().order ();
806 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
808 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
810 if (artv && artv != rtv) {
812 pair<set<RouteTimeAxisView*>::iterator,bool> result;
814 result = already_in_selection.insert (artv);
817 /* newly added to already_in_selection */
819 int d = artv->route()->presentation_info().order ();
823 if (abs (d) < distance) {
833 /* now add all tracks between that one and this one */
835 int okey = closest->route()->presentation_info().order ();
841 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
842 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
843 if (artv && artv != rtv) {
845 int k = artv->route()->presentation_info().order ();
847 if (k >= okey && k <= key) {
849 /* in range but don't add it if
850 it already has tracks selected.
851 this avoids odd selection
852 behaviour that feels wrong.
855 if (find (already_in_selection.begin(),
856 already_in_selection.end(),
857 artv) == already_in_selection.end()) {
859 relevant_tracks.insert (artv);
869 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
870 one that was clicked.
873 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
874 (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
877 /* 4. convert to a vector of regions */
879 vector<RegionView*> regions;
881 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
884 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
885 regions.push_back (arv);
889 if (!regions.empty()) {
890 selection->add (regions);
892 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
893 /* ensure that at least the clicked regionview is selected. */
894 selection->set (clicked_regionview);
905 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
910 begin_reversible_selection_op (X_("set selection"));
912 case Selection::Toggle:
913 selection->toggle (s);
918 case Selection::Extend:
926 commit_reversible_selection_op () ;
930 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
932 vector<RegionView*> all_equivalent_regions;
934 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
936 if (all_equivalent_regions.empty()) {
940 begin_reversible_selection_op (X_("set selected regions"));
943 case Selection::Toggle:
944 /* XXX this is not correct */
945 selection->toggle (all_equivalent_regions);
948 selection->set (all_equivalent_regions);
950 case Selection::Extend:
951 selection->add (all_equivalent_regions);
954 selection->add (all_equivalent_regions);
958 commit_reversible_selection_op () ;
962 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
965 boost::shared_ptr<Region> r (weak_r.lock());
971 if ((rv = sv->find_view (r)) == 0) {
975 /* don't reset the selection if its something other than
976 a single other region.
979 if (selection->regions.size() > 1) {
983 begin_reversible_selection_op (X_("set selected regions"));
987 commit_reversible_selection_op () ;
993 Editor::presentation_info_changed (PropertyChange const & what_changed)
995 uint32_t n_tracks = 0;
996 uint32_t n_busses = 0;
998 uint32_t n_routes = 0;
999 uint32_t n_stripables = 0;
1001 /* We cannot ensure ordering of the handlers for
1002 * PresentationInfo::Changed, so we have to do everything in order
1003 * here, as a single handler.
1006 if (what_changed.contains (Properties::selected)) {
1007 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1008 (*i)->set_selected (false);
1009 (*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 && !_editor_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);
1050 if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1053 } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1056 } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1060 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1064 continue; /* impossible */
1067 if (!(*i).controllable) {
1069 /* "parent" track selected */
1070 tav->set_selected (true);
1071 tav->reshow_selection (selection->time);
1075 /* possibly a child */
1077 TimeAxisView::Children c = tav->get_child_list ();
1079 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1081 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1083 if (control != (*i).controllable) {
1087 (*j)->set_selected (true);
1088 (*j)->reshow_selection (selection->time);
1092 stripables->push_back ((*i).stripable);
1095 ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1096 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1097 ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1098 ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1099 ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1101 sensitize_the_right_region_actions (false);
1103 /* STEP 4: notify control protocols */
1105 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1107 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1108 uint32_t audio_track_cnt = 0;
1109 uint32_t midi_track_cnt = 0;
1111 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1112 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1115 if (atv->is_audio_track()) {
1120 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1123 if (mtv->is_midi_track()) {
1130 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1134 /* STEP 4: update EditorRoutes treeview */
1138 soh.add (Properties::selected);
1139 soh.add (Properties::order);
1140 soh.add (Properties::hidden);
1142 if (what_changed.contains (soh)) {
1143 _routes->sync_treeview_from_presentation_info (what_changed);
1148 Editor::track_selection_changed ()
1150 /* reset paste count, so the plaste location doesn't get incremented
1151 * if we want to paste in the same place, but different track. */
1154 if ( _session->solo_selection_active() )
1155 play_solo_selection(false);
1159 Editor::time_selection_changed ()
1161 /* XXX this is superficially inefficient. Hide the selection in all
1162 * tracks, then show it in all selected tracks.
1164 * However, if you investigate what this actually does, it isn't
1165 * anywhere nearly as bad as it may appear. Remember: nothing is
1166 * redrawn or even recomputed during these two loops - that only
1167 * happens when we next render ...
1170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1171 (*i)->hide_selection ();
1174 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1175 (*i)->show_selection (selection->time);
1178 if (selection->time.empty()) {
1179 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1181 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1184 /* propagate into backend, but only when there is no drag or we are at
1185 * the end of a drag, otherwise this is too expensive (could case a
1186 * locate per mouse motion event.
1189 if (_session && !_drags->active()) {
1190 if (selection->time.length() != 0) {
1191 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1193 _session->clear_range_selection ();
1198 /** Set all region actions to have a given sensitivity */
1200 Editor::sensitize_all_region_actions (bool s)
1202 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1204 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1205 (*i)->set_sensitive (s);
1208 _all_region_actions_sensitized = s;
1211 /** Sensitize region-based actions.
1213 * This method is called from whenever we leave the canvas, either by moving
1214 * the pointer out of it, or by popping up a context menu. See
1215 * Editor::{entered,left}_track_canvas() for details there.
1218 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1220 bool have_selection = false;
1221 bool have_entered = false;
1222 bool have_edit_point = false;
1223 bool have_selected_source = false;
1226 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1229 if (!selection->regions.empty()) {
1230 have_selection = true;
1231 rs = selection->regions;
1234 if (entered_regionview) {
1235 have_entered = true;
1236 rs.add (entered_regionview);
1239 if ( _sources->get_single_selection() ) {
1240 have_selected_source = true;
1243 if (rs.empty() && !selection->tracks.empty()) {
1245 /* no selected regions, but some selected tracks.
1248 if (_edit_point == EditAtMouse) {
1249 if (!within_track_canvas) {
1250 /* pointer is not in canvas, so edit point is meaningless */
1251 have_edit_point = false;
1253 /* inside canvas. we don't know where the edit
1254 point will be when an action is invoked, but
1255 assume it could intersect with a region.
1257 have_edit_point = true;
1260 RegionSelection at_edit_point;
1261 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1262 get_regions_at (at_edit_point, where, selection->tracks);
1263 if (!at_edit_point.empty()) {
1264 have_edit_point = true;
1267 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1272 //std::cerr << "\tfinal have selection: " << have_selection
1273 // << " have entered " << have_entered
1274 // << " have edit point " << have_edit_point
1275 // << " EP = " << enum_2_string (_edit_point)
1278 typedef std::map<std::string,RegionAction> RegionActionMap;
1280 _ignore_region_action = true;
1282 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1283 RegionActionTarget tgt = x->second.target;
1284 bool sensitive = false;
1286 if ((tgt & SelectedRegions) && have_selection) {
1288 } else if ((tgt & EnteredRegions) && have_entered) {
1290 } else if ((tgt & EditPointRegions) && have_edit_point) {
1292 } else if ((tgt & ListSelection) && have_selected_source ) {
1296 x->second.action->set_sensitive (sensitive);
1299 /* Look through the regions that are selected and make notes about what we have got */
1301 bool have_audio = false;
1302 bool have_multichannel_audio = false;
1303 bool have_midi = false;
1304 bool have_locked = false;
1305 bool have_unlocked = false;
1306 bool have_video_locked = false;
1307 bool have_video_unlocked = false;
1308 bool have_position_lock_style_audio = false;
1309 bool have_position_lock_style_music = false;
1310 bool have_muted = false;
1311 bool have_unmuted = false;
1312 bool have_opaque = false;
1313 bool have_non_opaque = false;
1314 bool have_not_at_natural_position = false;
1315 bool have_envelope_active = false;
1316 bool have_envelope_inactive = false;
1317 bool have_non_unity_scale_amplitude = false;
1318 bool have_compound_regions = false;
1319 bool have_inactive_fade_in = false;
1320 bool have_inactive_fade_out = false;
1321 bool have_active_fade_in = false;
1322 bool have_active_fade_out = false;
1323 bool have_transients = false;
1325 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1327 boost::shared_ptr<Region> r = (*i)->region ();
1328 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1332 if (ar->n_channels() > 1) {
1333 have_multichannel_audio = true;
1337 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1341 if (r->is_compound()) {
1342 have_compound_regions = true;
1348 have_unlocked = true;
1351 if (r->video_locked()) {
1352 have_video_locked = true;
1354 have_video_unlocked = true;
1357 if (r->position_lock_style() == MusicTime) {
1358 have_position_lock_style_music = true;
1360 have_position_lock_style_audio = true;
1366 have_unmuted = true;
1372 have_non_opaque = true;
1375 if (!r->at_natural_position()) {
1376 have_not_at_natural_position = true;
1379 if (r->has_transients ()){
1380 have_transients = true;
1384 if (ar->envelope_active()) {
1385 have_envelope_active = true;
1387 have_envelope_inactive = true;
1390 if (ar->scale_amplitude() != 1) {
1391 have_non_unity_scale_amplitude = true;
1394 if (ar->fade_in_active ()) {
1395 have_active_fade_in = true;
1397 have_inactive_fade_in = true;
1400 if (ar->fade_out_active ()) {
1401 have_active_fade_out = true;
1403 have_inactive_fade_out = true;
1408 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1410 if (rs.size() > 1) {
1411 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1412 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1413 _region_actions->get_action("rename-region")->set_sensitive (false);
1415 /* XXX need to check whether there is than 1 per
1416 playlist, because otherwise this makes no sense.
1418 _region_actions->get_action("combine-regions")->set_sensitive (true);
1420 _region_actions->get_action("combine-regions")->set_sensitive (false);
1422 } else if (rs.size() == 1) {
1423 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1424 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1425 _region_actions->get_action("combine-regions")->set_sensitive (false);
1428 if (!have_multichannel_audio) {
1429 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1433 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1434 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1435 _region_actions->get_action("quantize-region")->set_sensitive (false);
1436 _region_actions->get_action("legatize-region")->set_sensitive (false);
1437 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1438 _region_actions->get_action("transform-region")->set_sensitive (false);
1439 _region_actions->get_action("fork-region")->set_sensitive (false);
1440 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1441 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1442 _region_actions->get_action("transpose-region")->set_sensitive (false);
1444 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1445 /* others were already marked sensitive */
1448 /* ok, moving along... */
1450 if (have_compound_regions) {
1451 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1453 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1458 if (have_envelope_active && !have_envelope_inactive) {
1459 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1460 } else if (have_envelope_active && have_envelope_inactive) {
1461 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1466 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1467 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1468 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1469 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1470 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1471 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1472 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1476 if (!have_non_unity_scale_amplitude || !have_audio) {
1477 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1480 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1481 a->set_active (have_locked && !have_unlocked);
1482 if (have_locked && have_unlocked) {
1483 // a->set_inconsistent ();
1486 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1487 a->set_active (have_video_locked && !have_video_unlocked);
1488 if (have_video_locked && have_video_unlocked) {
1489 // a->set_inconsistent ();
1492 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1493 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1495 vector<Widget*> proxies = a->get_proxies();
1496 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1497 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1499 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1503 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1504 a->set_active (have_muted && !have_unmuted);
1505 if (have_muted && have_unmuted) {
1506 // a->set_inconsistent ();
1509 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1510 a->set_active (have_opaque && !have_non_opaque);
1511 if (have_opaque && have_non_opaque) {
1512 // a->set_inconsistent ();
1515 if (!have_not_at_natural_position) {
1516 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1519 /* Todo: insert-region-from-source-list
1520 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1522 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1523 _region_actions->get_action("insert-region-from-source-list")->set_sensitive (false);
1525 _region_actions->get_action("insert-region-from-source-list")->set_sensitive (true);
1529 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1530 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1531 if (have_active_fade_in && have_inactive_fade_in) {
1532 // a->set_inconsistent ();
1535 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1536 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1538 if (have_active_fade_out && have_inactive_fade_out) {
1539 // a->set_inconsistent ();
1542 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1543 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1545 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1546 a->set_active (have_active_fade && !have_inactive_fade);
1548 if (have_active_fade && have_inactive_fade) {
1549 // a->set_inconsistent ();
1552 _ignore_region_action = false;
1554 _all_region_actions_sensitized = false;
1558 Editor::region_selection_changed ()
1560 _regions->block_change_connection (true);
1561 editor_regions_selection_changed_connection.block(true);
1563 if (_region_selection_change_updates_region_list) {
1564 _regions->unselect_all ();
1567 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1568 (*i)->set_selected_regionviews (selection->regions);
1571 if (_region_selection_change_updates_region_list) {
1572 _regions->set_selected (selection->regions);
1575 _regions->block_change_connection (false);
1576 editor_regions_selection_changed_connection.block(false);
1578 sensitize_the_right_region_actions (false);
1580 /* propagate into backend */
1583 if (!selection->regions.empty()) {
1584 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1586 _session->clear_object_selection ();
1590 if (_session->solo_selection_active()) {
1591 play_solo_selection(false);
1594 /* set nudge button color */
1595 if (! get_regions_from_selection_and_entered().empty()) {
1597 nudge_forward_button.set_name ("nudge button");
1598 nudge_backward_button.set_name ("nudge button");
1600 /* nudge marker or playhead */
1601 nudge_forward_button.set_name ("generic button");
1602 nudge_backward_button.set_name ("generic button");
1605 //there are a few global Editor->Select actions which select regions even if you aren't in Object mode.
1606 //if regions are selected, we must always force the mouse mode to Object...
1607 //... otherwise the user is confusingly left with selected regions that can't be manipulated.
1608 if (!selection->regions.empty()) {
1609 set_mouse_mode( MouseObject, false );
1614 Editor::point_selection_changed ()
1616 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1617 (*i)->set_selected_points (selection->points);
1622 Editor::select_all_in_track (Selection::Operation op)
1624 list<Selectable *> touched;
1626 if (!clicked_routeview) {
1630 begin_reversible_selection_op (X_("Select All in Track"));
1632 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1635 case Selection::Toggle:
1636 selection->add (touched);
1638 case Selection::Set:
1639 selection->set (touched);
1641 case Selection::Extend:
1642 /* meaningless, because we're selecting everything */
1644 case Selection::Add:
1645 selection->add (touched);
1649 commit_reversible_selection_op ();
1653 Editor::select_all_internal_edit (Selection::Operation)
1655 bool selected = false;
1657 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1658 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1660 mrv->select_all_notes ();
1665 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1667 mrv->select_all_notes ();
1675 Editor::select_all_objects (Selection::Operation op)
1677 list<Selectable *> touched;
1679 if (internal_editing() && select_all_internal_edit(op)) {
1680 return; // Selected notes
1685 if (selection->tracks.empty()) {
1688 ts = selection->tracks;
1691 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1692 if ((*iter)->hidden()) {
1695 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1698 begin_reversible_selection_op (X_("select all"));
1700 case Selection::Add:
1701 selection->add (touched);
1703 case Selection::Toggle:
1704 selection->toggle (touched);
1706 case Selection::Set:
1707 selection->set (touched);
1709 case Selection::Extend:
1710 /* meaningless, because we're selecting everything */
1713 commit_reversible_selection_op ();
1717 Editor::invert_selection_in_track ()
1719 list<Selectable *> touched;
1721 if (!clicked_routeview) {
1725 begin_reversible_selection_op (X_("Invert Selection in Track"));
1726 clicked_routeview->get_inverted_selectables (*selection, touched);
1727 selection->set (touched);
1728 commit_reversible_selection_op ();
1732 Editor::invert_selection ()
1735 if (internal_editing()) {
1736 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1737 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1739 mrv->invert_selection ();
1745 if (!selection->tracks.empty()) {
1747 TrackViewList inverted;
1749 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1750 if (!(*iter)->selected()) {
1751 inverted.push_back (*iter);
1755 begin_reversible_selection_op (X_("Invert Track Selection"));
1756 selection->set (inverted);
1757 commit_reversible_selection_op ();
1761 list<Selectable *> touched;
1763 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1764 if ((*iter)->hidden()) {
1767 (*iter)->get_inverted_selectables (*selection, touched);
1770 begin_reversible_selection_op (X_("Invert ObjectSelection"));
1771 selection->set (touched);
1772 commit_reversible_selection_op ();
1776 /** @param start Start time in session samples.
1777 * @param end End time in session samples.
1778 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1779 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1780 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1781 * within the region are already selected.
1784 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1786 list<Selectable*> found;
1788 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1790 if ((*iter)->hidden()) {
1794 (*iter)->get_selectables (start, end, top, bot, found);
1797 if (found.empty()) {
1798 selection->clear_objects();
1799 selection->clear_time ();
1803 if (preserve_if_selected && op != Selection::Toggle) {
1804 list<Selectable*>::iterator i = found.begin();
1805 while (i != found.end() && (*i)->selected()) {
1809 if (i == found.end()) {
1814 begin_reversible_selection_op (X_("select all within"));
1816 case Selection::Add:
1817 selection->add (found);
1819 case Selection::Toggle:
1820 selection->toggle (found);
1822 case Selection::Set:
1823 selection->set (found);
1825 case Selection::Extend:
1826 /* not defined yet */
1830 commit_reversible_selection_op ();
1834 Editor::set_selection_from_region ()
1836 if (selection->regions.empty()) {
1840 /* find all the tracks that have selected regions */
1842 set<TimeAxisView*> tracks;
1844 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1845 tracks.insert (&(*r)->get_time_axis_view());
1849 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1851 /* select range (this will clear the region selection) */
1853 selection->set (selection->regions.start(), selection->regions.end_sample());
1855 /* and select the tracks */
1857 selection->set (tvl);
1859 if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1860 set_mouse_mode (Editing::MouseRange, false);
1865 Editor::set_selection_from_punch()
1869 if ((location = _session->locations()->auto_punch_location()) == 0) {
1873 set_selection_from_range (*location);
1877 Editor::set_selection_from_loop()
1881 if ((location = _session->locations()->auto_loop_location()) == 0) {
1884 set_selection_from_range (*location);
1888 Editor::set_selection_from_range (Location& loc)
1890 begin_reversible_selection_op (X_("set selection from range"));
1892 selection->set (loc.start(), loc.end());
1894 // if no tracks are selected, enable all tracks
1895 // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1896 if (selection->tracks.empty()) {
1897 select_all_tracks();
1900 commit_reversible_selection_op ();
1902 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1903 set_mouse_mode (MouseRange, false);
1908 Editor::select_all_selectables_using_time_selection ()
1910 list<Selectable *> touched;
1912 if (selection->time.empty()) {
1916 samplepos_t start = selection->time[clicked_selection].start;
1917 samplepos_t end = selection->time[clicked_selection].end;
1919 if (end - start < 1) {
1925 if (selection->tracks.empty()) {
1928 ts = &selection->tracks;
1931 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1932 if ((*iter)->hidden()) {
1935 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1938 begin_reversible_selection_op (X_("select all from range"));
1939 selection->set (touched);
1940 commit_reversible_selection_op ();
1945 Editor::select_all_selectables_using_punch()
1947 Location* location = _session->locations()->auto_punch_location();
1948 list<Selectable *> touched;
1950 if (location == 0 || (location->end() - location->start() <= 1)) {
1957 if (selection->tracks.empty()) {
1960 ts = &selection->tracks;
1963 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1964 if ((*iter)->hidden()) {
1967 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1969 begin_reversible_selection_op (X_("select all from punch"));
1970 selection->set (touched);
1971 commit_reversible_selection_op ();
1976 Editor::select_all_selectables_using_loop()
1978 Location* location = _session->locations()->auto_loop_location();
1979 list<Selectable *> touched;
1981 if (location == 0 || (location->end() - location->start() <= 1)) {
1988 if (selection->tracks.empty()) {
1991 ts = &selection->tracks;
1994 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1995 if ((*iter)->hidden()) {
1998 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
2000 begin_reversible_selection_op (X_("select all from loop"));
2001 selection->set (touched);
2002 commit_reversible_selection_op ();
2007 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
2011 list<Selectable *> touched;
2014 start = cursor->current_sample();
2015 end = _session->current_end_sample();
2017 if (cursor->current_sample() > 0) {
2019 end = cursor->current_sample() - 1;
2025 if (internal_editing()) {
2026 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2027 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2029 mrv->select_range (start, end);
2036 begin_reversible_selection_op (X_("select all after cursor"));
2038 begin_reversible_selection_op (X_("select all before cursor"));
2043 if (selection->tracks.empty()) {
2046 ts = &selection->tracks;
2049 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2050 if ((*iter)->hidden()) {
2053 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2055 selection->set (touched);
2056 commit_reversible_selection_op ();
2060 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2064 list<Selectable *> touched;
2067 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2068 end = _session->current_end_sample();
2070 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2078 if (internal_editing()) {
2079 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2080 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2081 mrv->select_range (start, end);
2087 begin_reversible_selection_op (X_("select all after edit"));
2089 begin_reversible_selection_op (X_("select all before edit"));
2094 if (selection->tracks.empty()) {
2097 ts = &selection->tracks;
2100 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2101 if ((*iter)->hidden()) {
2104 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2106 selection->set (touched);
2107 commit_reversible_selection_op ();
2111 Editor::select_all_selectables_between (bool within)
2115 list<Selectable *> touched;
2117 if (!get_edit_op_range (start, end)) {
2121 if (internal_editing()) {
2122 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2123 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2124 mrv->select_range (start, end);
2131 if (selection->tracks.empty()) {
2134 ts = &selection->tracks;
2137 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2138 if ((*iter)->hidden()) {
2141 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2144 begin_reversible_selection_op (X_("Select all Selectables Between"));
2145 selection->set (touched);
2146 commit_reversible_selection_op ();
2150 Editor::select_range_between ()
2155 if (!selection->time.empty()) {
2156 selection->clear_time ();
2159 if (!get_edit_op_range (start, end)) {
2163 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2164 set_mouse_mode (MouseRange, false);
2167 begin_reversible_selection_op (X_("Select Range Between"));
2168 selection->set (start, end);
2169 commit_reversible_selection_op ();
2173 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2175 /* if an explicit range exists, use it */
2177 if ((mouse_mode == MouseRange || get_smart_mode()) && !selection->time.empty()) {
2178 /* we know that these are ordered */
2179 start = selection->time.start();
2180 end = selection->time.end_sample();
2190 Editor::deselect_all ()
2192 begin_reversible_selection_op (X_("Deselect All"));
2193 selection->clear ();
2194 commit_reversible_selection_op ();
2198 Editor::select_range (samplepos_t s, samplepos_t e)
2200 begin_reversible_selection_op (X_("Select Range"));
2201 selection->add (clicked_axisview);
2202 selection->time.clear ();
2203 long ret = selection->set (s, e);
2204 commit_reversible_selection_op ();