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"
33 #include "ardour/vca.h"
36 #include "editor_drag.h"
37 #include "editor_routes.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "audio_streamview.h"
42 #include "automation_line.h"
43 #include "control_point.h"
44 #include "editor_regions.h"
45 #include "editor_cursors.h"
46 #include "midi_region_view.h"
52 using namespace ARDOUR;
56 using namespace Gtkmm2ext;
57 using namespace Editing;
59 struct TrackViewByPositionSorter
61 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
62 return a->y_position() < b->y_position();
67 Editor::extend_selection_to_track (TimeAxisView& view)
69 if (selection->selected (&view)) {
70 /* already selected, do nothing */
74 if (selection->tracks.empty()) {
76 if (!selection->selected (&view)) {
77 selection->set (&view);
84 /* something is already selected, so figure out which range of things to add */
86 TrackViewList to_be_added;
87 TrackViewList sorted = track_views;
88 TrackViewByPositionSorter cmp;
89 bool passed_clicked = false;
94 /* figure out if we should go forward or backwards */
96 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
99 passed_clicked = true;
102 if (selection->selected (*i)) {
103 if (passed_clicked) {
112 passed_clicked = false;
116 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
119 passed_clicked = true;
123 if (passed_clicked) {
124 if ((*i)->hidden()) {
127 if (selection->selected (*i)) {
129 } else if (!(*i)->hidden()) {
130 to_be_added.push_back (*i);
137 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
140 passed_clicked = true;
144 if (passed_clicked) {
146 if ((*r)->hidden()) {
150 if (selection->selected (*r)) {
152 } else if (!(*r)->hidden()) {
153 to_be_added.push_back (*r);
159 if (!selection->selected (&view)) {
160 to_be_added.push_back (&view);
163 if (!to_be_added.empty()) {
164 selection->add (to_be_added);
172 Editor::select_all_tracks ()
174 TrackViewList visible_views;
175 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
176 if ((*i)->marked_for_display()) {
177 visible_views.push_back (*i);
180 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
181 selection->set (visible_views);
184 /** Select clicked_axisview, unless there are no currently selected
185 * tracks, in which case nothing will happen unless `force' is true.
188 Editor::set_selected_track_as_side_effect (Selection::Operation op)
190 if (!clicked_axisview) {
194 RouteGroup* group = NULL;
195 if (clicked_routeview) {
196 group = clicked_routeview->route()->route_group();
200 case Selection::Toggle:
201 if (selection->selected (clicked_axisview)) {
202 if (group && group->is_active()) {
203 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
204 if ((*i)->route_group() == group) {
205 selection->remove(*i);
209 selection->remove (clicked_axisview);
212 if (group && group->is_active()) {
213 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
214 if ((*i)->route_group() == group) {
219 selection->add (clicked_axisview);
225 if (group && group->is_active()) {
226 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
227 if ((*i)->route_group() == group) {
232 selection->add (clicked_axisview);
238 if (group && group->is_active()) {
239 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
240 if ((*i)->route_group() == group) {
245 selection->set (clicked_axisview);
249 case Selection::Extend:
256 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
258 begin_reversible_selection_op (X_("Set Selected Track"));
261 case Selection::Toggle:
262 if (selection->selected (&view)) {
264 selection->remove (&view);
267 selection->add (&view);
272 selection->add (&view);
276 selection->set (&view);
279 case Selection::Extend:
280 extend_selection_to_track (view);
284 commit_reversible_selection_op ();
288 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
290 if (!clicked_routeview) {
298 set_selected_track (*clicked_routeview, op, no_remove);
302 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
304 if (!clicked_control_point) {
312 if (!selection->selected (clicked_control_point)) {
313 selection->set (clicked_control_point);
316 /* clicked on an already selected point */
320 if (selection->points.size() > 1) {
321 selection->set (clicked_control_point);
330 selection->add (clicked_control_point);
334 case Selection::Toggle:
336 /* This is a bit of a hack; if we Primary-Click-Drag a control
337 point (for push drag) we want the point we clicked on to be
338 selected, otherwise we end up confusingly dragging an
339 unselected point. So here we ensure that the point is selected
340 after the press, and if we subsequently get a release (meaning no
341 drag occurred) we set things up so that the toggle has happened.
343 if (press && !selection->selected (clicked_control_point)) {
344 /* This is the button press, and the control point is not selected; make it so,
345 in case this press leads to a drag. Also note that having done this, we don't
346 need to toggle again on release.
348 selection->toggle (clicked_control_point);
349 _control_point_toggled_on_press = true;
351 } else if (!press && !_control_point_toggled_on_press) {
352 /* This is the release, and the point wasn't toggled on the press, so do it now */
353 selection->toggle (clicked_control_point);
357 _control_point_toggled_on_press = false;
360 case Selection::Extend:
369 Editor::get_onscreen_tracks (TrackViewList& tvl)
371 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
372 if ((*i)->y_position() < _visible_canvas_height) {
378 /** Call a slot for a given `basis' track and also for any track that is in the same
379 * active route group with a particular set of properties.
381 * @param sl Slot to call.
382 * @param basis Basis track.
383 * @param prop Properties that active edit groups must share to be included in the map.
387 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
389 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
391 if (route_basis == 0) {
395 set<RouteTimeAxisView*> tracks;
396 tracks.insert (route_basis);
398 RouteGroup* group = route_basis->route()->route_group();
400 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
402 /* the basis is a member of an active route group, with the appropriate
403 properties; find other members */
405 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
406 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
407 if (v && v->route()->route_group() == group) {
414 uint32_t const sz = tracks.size ();
416 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
421 /** Call a slot for a given `basis' track and also for any track that is in the same
422 * active route group with a particular set of properties.
424 * @param sl Slot to call.
425 * @param basis Basis track.
426 * @param prop Properties that active edit groups must share to be included in the map.
430 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
432 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
433 set<boost::shared_ptr<Playlist> > playlists;
435 if (route_basis == 0) {
439 set<RouteTimeAxisView*> tracks;
440 tracks.insert (route_basis);
442 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
444 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
446 /* the basis is a member of an active route group, with the appropriate
447 properties; find other members */
449 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
450 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
452 if (v && v->route()->route_group() == group) {
454 boost::shared_ptr<Track> t = v->track();
456 if (playlists.insert (t->playlist()).second) {
457 /* haven't seen this playlist yet */
461 /* not actually a "Track", but a timeaxis view that
462 we should mapover anyway.
471 uint32_t const sz = tracks.size ();
473 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
479 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
481 boost::shared_ptr<Playlist> pl;
482 vector<boost::shared_ptr<Region> > results;
484 boost::shared_ptr<Track> tr;
486 if ((tr = tv.track()) == 0) {
491 if (&tv == &basis->get_time_axis_view()) {
492 /* looking in same track as the original */
496 if ((pl = tr->playlist()) != 0) {
497 pl->get_equivalent_regions (basis->region(), results);
500 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
501 if ((marv = tv.view()->find_view (*ir)) != 0) {
502 all_equivs->push_back (marv);
508 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
510 mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
512 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
514 equivalent_regions.push_back (basis);
518 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
520 RegionSelection equivalent;
522 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
524 vector<RegionView*> eq;
526 mapover_tracks_with_unique_playlists (
527 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
528 &(*i)->get_time_axis_view(), prop);
530 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
541 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
543 int region_count = 0;
545 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
547 RouteTimeAxisView* tatv;
549 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
551 boost::shared_ptr<Playlist> pl;
552 vector<boost::shared_ptr<Region> > results;
554 boost::shared_ptr<Track> tr;
556 if ((tr = tatv->track()) == 0) {
561 if ((pl = (tr->playlist())) != 0) {
562 pl->get_region_list_equivalent_regions (region, results);
565 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
566 if ((marv = tatv->view()->find_view (*ir)) != 0) {
579 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
581 vector<RegionView*> all_equivalent_regions;
584 if (!clicked_regionview || !clicked_routeview) {
589 button_release_can_deselect = false;
592 if (op == Selection::Toggle || op == Selection::Set) {
595 case Selection::Toggle:
596 if (selection->selected (clicked_regionview)) {
599 /* whatever was clicked was selected already; do nothing here but allow
600 the button release to deselect it
603 button_release_can_deselect = true;
606 if (button_release_can_deselect) {
608 /* just remove this one region, but only on a permitted button release */
610 selection->remove (clicked_regionview);
613 /* no more deselect action on button release till a new press
614 finds an already selected object.
617 button_release_can_deselect = false;
625 if (selection->selected (clicked_routeview)) {
626 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
628 all_equivalent_regions.push_back (clicked_regionview);
631 /* add all the equivalent regions, but only on button press */
633 if (!all_equivalent_regions.empty()) {
637 selection->add (all_equivalent_regions);
643 if (!selection->selected (clicked_regionview)) {
644 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
645 selection->set (all_equivalent_regions);
648 /* clicked on an already selected region */
652 if (selection->regions.size() > 1) {
653 /* collapse region selection down to just this one region (and its equivalents) */
654 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
655 selection->set(all_equivalent_regions);
667 } else if (op == Selection::Extend) {
669 list<Selectable*> results;
670 samplepos_t last_sample;
671 samplepos_t first_sample;
672 bool same_track = false;
674 /* 1. find the last selected regionview in the track that was clicked in */
677 first_sample = max_samplepos;
679 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
680 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
682 if ((*x)->region()->last_sample() > last_sample) {
683 last_sample = (*x)->region()->last_sample();
686 if ((*x)->region()->first_sample() < first_sample) {
687 first_sample = (*x)->region()->first_sample();
696 /* 2. figure out the boundaries for our search for new objects */
698 switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
699 case Evoral::OverlapNone:
700 if (last_sample < clicked_regionview->region()->first_sample()) {
701 first_sample = last_sample;
702 last_sample = clicked_regionview->region()->last_sample();
704 last_sample = first_sample;
705 first_sample = clicked_regionview->region()->first_sample();
709 case Evoral::OverlapExternal:
710 if (last_sample < clicked_regionview->region()->first_sample()) {
711 first_sample = last_sample;
712 last_sample = clicked_regionview->region()->last_sample();
714 last_sample = first_sample;
715 first_sample = clicked_regionview->region()->first_sample();
719 case Evoral::OverlapInternal:
720 if (last_sample < clicked_regionview->region()->first_sample()) {
721 first_sample = last_sample;
722 last_sample = clicked_regionview->region()->last_sample();
724 last_sample = first_sample;
725 first_sample = clicked_regionview->region()->first_sample();
729 case Evoral::OverlapStart:
730 case Evoral::OverlapEnd:
731 /* nothing to do except add clicked region to selection, since it
732 overlaps with the existing selection in this track.
739 /* click in a track that has no regions selected, so extend vertically
740 to pick out all regions that are defined by the existing selection
745 first_sample = clicked_regionview->region()->position();
746 last_sample = clicked_regionview->region()->last_sample();
748 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
749 if ((*i)->region()->position() < first_sample) {
750 first_sample = (*i)->region()->position();
752 if ((*i)->region()->last_sample() + 1 > last_sample) {
753 last_sample = (*i)->region()->last_sample();
758 /* 2. find all the tracks we should select in */
760 set<RouteTimeAxisView*> relevant_tracks;
762 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
763 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
765 relevant_tracks.insert (r);
769 set<RouteTimeAxisView*> already_in_selection;
771 if (relevant_tracks.empty()) {
773 /* no tracks selected .. thus .. if the
774 regionview we're in isn't selected
775 (i.e. we're about to extend to it), then
776 find all tracks between the this one and
780 if (!selection->selected (clicked_regionview)) {
782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
786 /* add this track to the ones we will search */
788 relevant_tracks.insert (rtv);
790 /* find the track closest to this one that
791 already a selected region.
794 RouteTimeAxisView* closest = 0;
795 int distance = INT_MAX;
796 int key = rtv->route()->presentation_info().order ();
798 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
800 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
802 if (artv && artv != rtv) {
804 pair<set<RouteTimeAxisView*>::iterator,bool> result;
806 result = already_in_selection.insert (artv);
809 /* newly added to already_in_selection */
811 int d = artv->route()->presentation_info().order ();
815 if (abs (d) < distance) {
825 /* now add all tracks between that one and this one */
827 int okey = closest->route()->presentation_info().order ();
833 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
834 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
835 if (artv && artv != rtv) {
837 int k = artv->route()->presentation_info().order ();
839 if (k >= okey && k <= key) {
841 /* in range but don't add it if
842 it already has tracks selected.
843 this avoids odd selection
844 behaviour that feels wrong.
847 if (find (already_in_selection.begin(),
848 already_in_selection.end(),
849 artv) == already_in_selection.end()) {
851 relevant_tracks.insert (artv);
861 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
862 one that was clicked.
865 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
866 (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
869 /* 4. convert to a vector of regions */
871 vector<RegionView*> regions;
873 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
876 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
877 regions.push_back (arv);
881 if (!regions.empty()) {
882 selection->add (regions);
884 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
885 /* ensure that at least the clicked regionview is selected. */
886 selection->set (clicked_regionview);
897 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
902 begin_reversible_selection_op (X_("set selection"));
904 case Selection::Toggle:
905 selection->toggle (s);
910 case Selection::Extend:
918 commit_reversible_selection_op () ;
922 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
924 vector<RegionView*> all_equivalent_regions;
926 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
928 if (all_equivalent_regions.empty()) {
932 begin_reversible_selection_op (X_("set selected regions"));
935 case Selection::Toggle:
936 /* XXX this is not correct */
937 selection->toggle (all_equivalent_regions);
940 selection->set (all_equivalent_regions);
942 case Selection::Extend:
943 selection->add (all_equivalent_regions);
946 selection->add (all_equivalent_regions);
950 commit_reversible_selection_op () ;
954 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
957 boost::shared_ptr<Region> r (weak_r.lock());
963 if ((rv = sv->find_view (r)) == 0) {
967 /* don't reset the selection if its something other than
968 a single other region.
971 if (selection->regions.size() > 1) {
975 begin_reversible_selection_op (X_("set selected regions"));
979 commit_reversible_selection_op () ;
984 struct SelectionOrderSorter {
985 bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const {
986 boost::shared_ptr<Stripable> sa = a->stripable ();
987 boost::shared_ptr<Stripable> sb = b->stripable ();
997 return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
1002 Editor::presentation_info_changed (PropertyChange const & what_changed)
1004 uint32_t n_tracks = 0;
1005 uint32_t n_busses = 0;
1006 uint32_t n_vcas = 0;
1007 uint32_t n_routes = 0;
1008 uint32_t n_stripables = 0;
1010 /* We cannot ensure ordering of the handlers for
1011 * PresentationInfo::Changed, so we have to do everything in order
1012 * here, as a single handler.
1015 if (what_changed.contains (Properties::selected)) {
1016 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1017 (*i)->set_selected (false);
1018 (*i)->hide_selection ();
1022 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1023 * currently selected stripable/controllable duples are found and added
1026 selection->core_selection_changed (what_changed);
1028 /* STEP 2: update TimeAxisView's knowledge of their selected state
1031 if (what_changed.contains (Properties::selected)) {
1033 StripableNotificationListPtr stripables (new StripableNotificationList);
1035 switch (selection->tracks.size()) {
1039 set_selected_mixer_strip (*(selection->tracks.back()));
1040 if (!_track_selection_change_without_scroll) {
1041 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1046 CoreSelection::StripableAutomationControls sc;
1047 _session->selection().get_stripables (sc);
1049 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1051 AxisView* av = axis_view_by_stripable ((*i).stripable);
1059 if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1062 } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1065 } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1069 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1073 continue; /* impossible */
1076 if (!(*i).controllable) {
1078 /* "parent" track selected */
1079 tav->set_selected (true);
1080 tav->reshow_selection (selection->time);
1084 /* possibly a child */
1086 TimeAxisView::Children c = tav->get_child_list ();
1088 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1090 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1092 if (control != (*i).controllable) {
1096 (*j)->set_selected (true);
1097 (*j)->reshow_selection (selection->time);
1101 stripables->push_back ((*i).stripable);
1104 ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1105 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1106 ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1107 ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1108 ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1110 sensitize_the_right_region_actions (false);
1112 /* STEP 4: notify control protocols */
1114 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1116 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1117 uint32_t audio_track_cnt = 0;
1118 uint32_t midi_track_cnt = 0;
1120 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1121 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1124 if (atv->is_audio_track()) {
1129 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1132 if (mtv->is_midi_track()) {
1139 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1143 /* STEP 4: update EditorRoutes treeview */
1147 soh.add (Properties::selected);
1148 soh.add (Properties::order);
1149 soh.add (Properties::hidden);
1151 if (what_changed.contains (soh)) {
1152 _routes->sync_treeview_from_presentation_info (what_changed);
1157 Editor::time_selection_changed ()
1159 /* XXX this is superficially inefficient. Hide the selection in all
1160 * tracks, then show it in all selected tracks.
1162 * However, if you investigate what this actually does, it isn't
1163 * anywhere nearly as bad as it may appear. Remember: nothing is
1164 * redrawn or even recomputed during these two loops - that only
1165 * happens when we next render ...
1168 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1169 (*i)->hide_selection ();
1172 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1173 (*i)->show_selection (selection->time);
1176 if (selection->time.empty()) {
1177 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1179 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1182 /* propagate into backend, but only when there is no drag or we are at
1183 * the end of a drag, otherwise this is too expensive (could case a
1184 * locate per mouse motion event.
1187 if (_session && !_drags->active()) {
1188 if (selection->time.length() != 0) {
1189 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1191 _session->clear_range_selection ();
1196 /** Set all region actions to have a given sensitivity */
1198 Editor::sensitize_all_region_actions (bool s)
1200 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1202 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1203 (*i)->set_sensitive (s);
1206 _all_region_actions_sensitized = s;
1209 /** Sensitize region-based actions.
1211 * This method is called from whenever we leave the canvas, either by moving
1212 * the pointer out of it, or by popping up a context menu. See
1213 * Editor::{entered,left}_track_canvas() for details there.
1216 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1218 bool have_selection = false;
1219 bool have_entered = false;
1220 bool have_edit_point = false;
1223 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1226 if (!selection->regions.empty()) {
1227 have_selection = true;
1228 rs = selection->regions;
1231 if (entered_regionview) {
1232 have_entered = true;
1233 rs.add (entered_regionview);
1236 if (rs.empty() && !selection->tracks.empty()) {
1238 /* no selected regions, but some selected tracks.
1241 if (_edit_point == EditAtMouse) {
1242 if (!within_track_canvas) {
1243 /* pointer is not in canvas, so edit point is meaningless */
1244 have_edit_point = false;
1246 /* inside canvas. we don't know where the edit
1247 point will be when an action is invoked, but
1248 assume it could intersect with a region.
1250 have_edit_point = true;
1253 RegionSelection at_edit_point;
1254 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1255 get_regions_at (at_edit_point, where, selection->tracks);
1256 if (!at_edit_point.empty()) {
1257 have_edit_point = true;
1260 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1265 //std::cerr << "\tfinal have selection: " << have_selection
1266 // << " have entered " << have_entered
1267 // << " have edit point " << have_edit_point
1268 // << " EP = " << enum_2_string (_edit_point)
1271 typedef std::map<std::string,RegionAction> RegionActionMap;
1273 _ignore_region_action = true;
1275 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1276 RegionActionTarget tgt = x->second.target;
1277 bool sensitive = false;
1279 if ((tgt & SelectedRegions) && have_selection) {
1281 } else if ((tgt & EnteredRegions) && have_entered) {
1283 } else if ((tgt & EditPointRegions) && have_edit_point) {
1287 x->second.action->set_sensitive (sensitive);
1290 /* Look through the regions that are selected and make notes about what we have got */
1292 bool have_audio = false;
1293 bool have_multichannel_audio = false;
1294 bool have_midi = false;
1295 bool have_locked = false;
1296 bool have_unlocked = false;
1297 bool have_video_locked = false;
1298 bool have_video_unlocked = false;
1299 bool have_position_lock_style_audio = false;
1300 bool have_position_lock_style_music = false;
1301 bool have_muted = false;
1302 bool have_unmuted = false;
1303 bool have_opaque = false;
1304 bool have_non_opaque = false;
1305 bool have_not_at_natural_position = false;
1306 bool have_envelope_active = false;
1307 bool have_envelope_inactive = false;
1308 bool have_non_unity_scale_amplitude = false;
1309 bool have_compound_regions = false;
1310 bool have_inactive_fade_in = false;
1311 bool have_inactive_fade_out = false;
1312 bool have_active_fade_in = false;
1313 bool have_active_fade_out = false;
1314 bool have_transients = false;
1316 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1318 boost::shared_ptr<Region> r = (*i)->region ();
1319 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1323 if (ar->n_channels() > 1) {
1324 have_multichannel_audio = true;
1328 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1332 if (r->is_compound()) {
1333 have_compound_regions = true;
1339 have_unlocked = true;
1342 if (r->video_locked()) {
1343 have_video_locked = true;
1345 have_video_unlocked = true;
1348 if (r->position_lock_style() == MusicTime) {
1349 have_position_lock_style_music = true;
1351 have_position_lock_style_audio = true;
1357 have_unmuted = true;
1363 have_non_opaque = true;
1366 if (!r->at_natural_position()) {
1367 have_not_at_natural_position = true;
1370 if (r->has_transients ()){
1371 have_transients = true;
1375 if (ar->envelope_active()) {
1376 have_envelope_active = true;
1378 have_envelope_inactive = true;
1381 if (ar->scale_amplitude() != 1) {
1382 have_non_unity_scale_amplitude = true;
1385 if (ar->fade_in_active ()) {
1386 have_active_fade_in = true;
1388 have_inactive_fade_in = true;
1391 if (ar->fade_out_active ()) {
1392 have_active_fade_out = true;
1394 have_inactive_fade_out = true;
1399 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1401 if (rs.size() > 1) {
1402 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1403 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1404 _region_actions->get_action("rename-region")->set_sensitive (false);
1406 /* XXX need to check whether there is than 1 per
1407 playlist, because otherwise this makes no sense.
1409 _region_actions->get_action("combine-regions")->set_sensitive (true);
1411 _region_actions->get_action("combine-regions")->set_sensitive (false);
1413 } else if (rs.size() == 1) {
1414 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1415 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1416 _region_actions->get_action("combine-regions")->set_sensitive (false);
1419 if (!have_multichannel_audio) {
1420 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1424 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1425 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1426 _region_actions->get_action("quantize-region")->set_sensitive (false);
1427 _region_actions->get_action("legatize-region")->set_sensitive (false);
1428 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1429 _region_actions->get_action("transform-region")->set_sensitive (false);
1430 _region_actions->get_action("fork-region")->set_sensitive (false);
1431 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1432 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1433 _region_actions->get_action("transpose-region")->set_sensitive (false);
1435 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1436 /* others were already marked sensitive */
1439 /* ok, moving along... */
1441 if (have_compound_regions) {
1442 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1444 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1449 if (have_envelope_active && !have_envelope_inactive) {
1450 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1451 } else if (have_envelope_active && have_envelope_inactive) {
1452 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1457 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1458 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1459 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1460 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1461 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1462 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1463 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1467 if (!have_non_unity_scale_amplitude || !have_audio) {
1468 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1471 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1472 a->set_active (have_locked && !have_unlocked);
1473 if (have_locked && have_unlocked) {
1474 // a->set_inconsistent ();
1477 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1478 a->set_active (have_video_locked && !have_video_unlocked);
1479 if (have_video_locked && have_video_unlocked) {
1480 // a->set_inconsistent ();
1483 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1484 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1486 vector<Widget*> proxies = a->get_proxies();
1487 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1488 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1490 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1494 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1495 a->set_active (have_muted && !have_unmuted);
1496 if (have_muted && have_unmuted) {
1497 // a->set_inconsistent ();
1500 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1501 a->set_active (have_opaque && !have_non_opaque);
1502 if (have_opaque && have_non_opaque) {
1503 // a->set_inconsistent ();
1506 if (!have_not_at_natural_position) {
1507 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1510 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1511 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1512 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1514 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1517 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1518 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1519 if (have_active_fade_in && have_inactive_fade_in) {
1520 // a->set_inconsistent ();
1523 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1524 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1526 if (have_active_fade_out && have_inactive_fade_out) {
1527 // a->set_inconsistent ();
1530 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1531 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1533 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1534 a->set_active (have_active_fade && !have_inactive_fade);
1536 if (have_active_fade && have_inactive_fade) {
1537 // a->set_inconsistent ();
1540 _ignore_region_action = false;
1542 _all_region_actions_sensitized = false;
1546 Editor::region_selection_changed ()
1548 _regions->block_change_connection (true);
1549 editor_regions_selection_changed_connection.block(true);
1551 if (_region_selection_change_updates_region_list) {
1552 _regions->unselect_all ();
1555 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1556 (*i)->set_selected_regionviews (selection->regions);
1559 if (_region_selection_change_updates_region_list) {
1560 _regions->set_selected (selection->regions);
1563 _regions->block_change_connection (false);
1564 editor_regions_selection_changed_connection.block(false);
1566 sensitize_the_right_region_actions (false);
1568 /* propagate into backend */
1571 if (!selection->regions.empty()) {
1572 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1574 _session->clear_object_selection ();
1581 Editor::point_selection_changed ()
1583 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1584 (*i)->set_selected_points (selection->points);
1589 Editor::select_all_in_track (Selection::Operation op)
1591 list<Selectable *> touched;
1593 if (!clicked_routeview) {
1597 begin_reversible_selection_op (X_("Select All in Track"));
1599 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1602 case Selection::Toggle:
1603 selection->add (touched);
1605 case Selection::Set:
1606 selection->set (touched);
1608 case Selection::Extend:
1609 /* meaningless, because we're selecting everything */
1611 case Selection::Add:
1612 selection->add (touched);
1616 commit_reversible_selection_op ();
1620 Editor::select_all_internal_edit (Selection::Operation)
1622 bool selected = false;
1624 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1625 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1627 mrv->select_all_notes ();
1632 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1634 mrv->select_all_notes ();
1642 Editor::select_all_objects (Selection::Operation op)
1644 list<Selectable *> touched;
1646 if (internal_editing() && select_all_internal_edit(op)) {
1647 return; // Selected notes
1652 if (selection->tracks.empty()) {
1655 ts = selection->tracks;
1658 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1659 if ((*iter)->hidden()) {
1662 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1665 begin_reversible_selection_op (X_("select all"));
1667 case Selection::Add:
1668 selection->add (touched);
1670 case Selection::Toggle:
1671 selection->toggle (touched);
1673 case Selection::Set:
1674 selection->set (touched);
1676 case Selection::Extend:
1677 /* meaningless, because we're selecting everything */
1680 commit_reversible_selection_op ();
1684 Editor::invert_selection_in_track ()
1686 list<Selectable *> touched;
1688 if (!clicked_routeview) {
1692 begin_reversible_selection_op (X_("Invert Selection in Track"));
1693 clicked_routeview->get_inverted_selectables (*selection, touched);
1694 selection->set (touched);
1695 commit_reversible_selection_op ();
1699 Editor::invert_selection ()
1701 list<Selectable *> touched;
1703 if (internal_editing()) {
1704 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1705 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1707 mrv->invert_selection ();
1713 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1714 if ((*iter)->hidden()) {
1717 (*iter)->get_inverted_selectables (*selection, touched);
1720 begin_reversible_selection_op (X_("Invert Selection"));
1721 selection->set (touched);
1722 commit_reversible_selection_op ();
1725 /** @param start Start time in session samples.
1726 * @param end End time in session samples.
1727 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1728 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1729 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1730 * within the region are already selected.
1733 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1735 list<Selectable*> found;
1737 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1739 if ((*iter)->hidden()) {
1743 (*iter)->get_selectables (start, end, top, bot, found);
1746 if (found.empty()) {
1747 selection->clear_objects();
1748 selection->clear_time ();
1752 if (preserve_if_selected && op != Selection::Toggle) {
1753 list<Selectable*>::iterator i = found.begin();
1754 while (i != found.end() && (*i)->selected()) {
1758 if (i == found.end()) {
1763 begin_reversible_selection_op (X_("select all within"));
1765 case Selection::Add:
1766 selection->add (found);
1768 case Selection::Toggle:
1769 selection->toggle (found);
1771 case Selection::Set:
1772 selection->set (found);
1774 case Selection::Extend:
1775 /* not defined yet */
1779 commit_reversible_selection_op ();
1783 Editor::set_selection_from_region ()
1785 if (selection->regions.empty()) {
1789 /* find all the tracks that have selected regions */
1791 set<TimeAxisView*> tracks;
1793 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1794 tracks.insert (&(*r)->get_time_axis_view());
1798 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1800 /* select range (this will clear the region selection) */
1802 selection->set (selection->regions.start(), selection->regions.end_sample());
1804 /* and select the tracks */
1806 selection->set (tvl);
1808 if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1809 set_mouse_mode (Editing::MouseRange, false);
1814 Editor::set_selection_from_punch()
1818 if ((location = _session->locations()->auto_punch_location()) == 0) {
1822 set_selection_from_range (*location);
1826 Editor::set_selection_from_loop()
1830 if ((location = _session->locations()->auto_loop_location()) == 0) {
1833 set_selection_from_range (*location);
1837 Editor::set_selection_from_range (Location& loc)
1839 begin_reversible_selection_op (X_("set selection from range"));
1840 selection->set (loc.start(), loc.end());
1841 commit_reversible_selection_op ();
1843 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1844 set_mouse_mode (MouseRange, false);
1849 Editor::select_all_selectables_using_time_selection ()
1851 list<Selectable *> touched;
1853 if (selection->time.empty()) {
1857 samplepos_t start = selection->time[clicked_selection].start;
1858 samplepos_t end = selection->time[clicked_selection].end;
1860 if (end - start < 1) {
1866 if (selection->tracks.empty()) {
1869 ts = &selection->tracks;
1872 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1873 if ((*iter)->hidden()) {
1876 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1879 begin_reversible_selection_op (X_("select all from range"));
1880 selection->set (touched);
1881 commit_reversible_selection_op ();
1886 Editor::select_all_selectables_using_punch()
1888 Location* location = _session->locations()->auto_punch_location();
1889 list<Selectable *> touched;
1891 if (location == 0 || (location->end() - location->start() <= 1)) {
1898 if (selection->tracks.empty()) {
1901 ts = &selection->tracks;
1904 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1905 if ((*iter)->hidden()) {
1908 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1910 begin_reversible_selection_op (X_("select all from punch"));
1911 selection->set (touched);
1912 commit_reversible_selection_op ();
1917 Editor::select_all_selectables_using_loop()
1919 Location* location = _session->locations()->auto_loop_location();
1920 list<Selectable *> touched;
1922 if (location == 0 || (location->end() - location->start() <= 1)) {
1929 if (selection->tracks.empty()) {
1932 ts = &selection->tracks;
1935 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1936 if ((*iter)->hidden()) {
1939 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1941 begin_reversible_selection_op (X_("select all from loop"));
1942 selection->set (touched);
1943 commit_reversible_selection_op ();
1948 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1952 list<Selectable *> touched;
1955 start = cursor->current_sample();
1956 end = _session->current_end_sample();
1958 if (cursor->current_sample() > 0) {
1960 end = cursor->current_sample() - 1;
1966 if (internal_editing()) {
1967 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1968 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1970 mrv->select_range (start, end);
1977 begin_reversible_selection_op (X_("select all after cursor"));
1979 begin_reversible_selection_op (X_("select all before cursor"));
1984 if (selection->tracks.empty()) {
1987 ts = &selection->tracks;
1990 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1991 if ((*iter)->hidden()) {
1994 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1996 selection->set (touched);
1997 commit_reversible_selection_op ();
2001 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2005 list<Selectable *> touched;
2008 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2009 end = _session->current_end_sample();
2011 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2019 if (internal_editing()) {
2020 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2021 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2022 mrv->select_range (start, end);
2028 begin_reversible_selection_op (X_("select all after edit"));
2030 begin_reversible_selection_op (X_("select all before edit"));
2035 if (selection->tracks.empty()) {
2038 ts = &selection->tracks;
2041 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2042 if ((*iter)->hidden()) {
2045 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2047 selection->set (touched);
2048 commit_reversible_selection_op ();
2052 Editor::select_all_selectables_between (bool within)
2056 list<Selectable *> touched;
2058 if (!get_edit_op_range (start, end)) {
2062 if (internal_editing()) {
2063 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2064 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2065 mrv->select_range (start, end);
2072 if (selection->tracks.empty()) {
2075 ts = &selection->tracks;
2078 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2079 if ((*iter)->hidden()) {
2082 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2085 begin_reversible_selection_op (X_("Select all Selectables Between"));
2086 selection->set (touched);
2087 commit_reversible_selection_op ();
2091 Editor::select_range_between ()
2096 if ( !selection->time.empty() ) {
2097 selection->clear_time ();
2100 if (!get_edit_op_range (start, end)) {
2104 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2105 set_mouse_mode (MouseRange, false);
2108 begin_reversible_selection_op (X_("Select Range Between"));
2109 selection->set (start, end);
2110 commit_reversible_selection_op ();
2114 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2119 /* if an explicit range exists, use it */
2121 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
2122 /* we know that these are ordered */
2123 start = selection->time.start();
2124 end = selection->time.end_sample();
2132 // if (!mouse_sample (m, ignored)) {
2133 // /* mouse is not in a canvas, try playhead+selected marker.
2134 // this is probably most true when using menus.
2137 // if (selection->markers.empty()) {
2141 // start = selection->markers.front()->position();
2142 // end = _session->audible_sample();
2146 // switch (_edit_point) {
2147 // case EditAtPlayhead:
2148 // if (selection->markers.empty()) {
2149 // /* use mouse + playhead */
2151 // end = _session->audible_sample();
2153 // /* use playhead + selected marker */
2154 // start = _session->audible_sample();
2155 // end = selection->markers.front()->position();
2159 // case EditAtMouse:
2160 // /* use mouse + selected marker */
2161 // if (selection->markers.empty()) {
2163 // end = _session->audible_sample();
2165 // start = selection->markers.front()->position();
2170 // case EditAtSelectedMarker:
2171 // /* use mouse + selected marker */
2172 // if (selection->markers.empty()) {
2174 // MessageDialog win (_("No edit range defined"),
2179 // win.set_secondary_text (
2180 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2183 // win.set_default_response (RESPONSE_CLOSE);
2184 // win.set_position (Gtk::WIN_POS_MOUSE);
2189 // return false; // NO RANGE
2191 // start = selection->markers.front()->position();
2197 // if (start == end) {
2201 // if (start > end) {
2202 // swap (start, end);
2205 /* turn range into one delimited by start...end,
2215 Editor::deselect_all ()
2217 begin_reversible_selection_op (X_("Deselect All"));
2218 selection->clear ();
2219 commit_reversible_selection_op ();
2223 Editor::select_range (samplepos_t s, samplepos_t e)
2225 begin_reversible_selection_op (X_("Select Range"));
2226 selection->add (clicked_axisview);
2227 selection->time.clear ();
2228 long ret = selection->set (s, e);
2229 commit_reversible_selection_op ();