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 PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
196 RouteGroup* group = NULL;
197 if (clicked_routeview) {
198 group = clicked_routeview->route()->route_group();
202 case Selection::Toggle:
203 if (selection->selected (clicked_axisview)) {
204 if (group && group->is_active()) {
205 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
206 if ((*i)->route_group() == group) {
207 selection->remove(*i);
211 selection->remove (clicked_axisview);
214 if (group && group->is_active()) {
215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216 if ((*i)->route_group() == group) {
221 selection->add (clicked_axisview);
227 if (group && group->is_active()) {
228 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
229 if ((*i)->route_group() == group) {
234 selection->add (clicked_axisview);
240 if (group && group->is_active()) {
241 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
242 if ((*i)->route_group() == group) {
247 selection->set (clicked_axisview);
251 case Selection::Extend:
258 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
260 begin_reversible_selection_op (X_("Set Selected Track"));
263 case Selection::Toggle:
264 if (selection->selected (&view)) {
266 selection->remove (&view);
269 selection->add (&view);
274 selection->add (&view);
278 selection->set (&view);
281 case Selection::Extend:
282 extend_selection_to_track (view);
286 commit_reversible_selection_op ();
290 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
292 if (!clicked_routeview) {
300 set_selected_track (*clicked_routeview, op, no_remove);
304 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
306 if (!clicked_control_point) {
314 if (!selection->selected (clicked_control_point)) {
315 selection->set (clicked_control_point);
318 /* clicked on an already selected point */
322 if (selection->points.size() > 1) {
323 selection->set (clicked_control_point);
332 selection->add (clicked_control_point);
336 case Selection::Toggle:
338 /* This is a bit of a hack; if we Primary-Click-Drag a control
339 point (for push drag) we want the point we clicked on to be
340 selected, otherwise we end up confusingly dragging an
341 unselected point. So here we ensure that the point is selected
342 after the press, and if we subsequently get a release (meaning no
343 drag occurred) we set things up so that the toggle has happened.
345 if (press && !selection->selected (clicked_control_point)) {
346 /* This is the button press, and the control point is not selected; make it so,
347 in case this press leads to a drag. Also note that having done this, we don't
348 need to toggle again on release.
350 selection->toggle (clicked_control_point);
351 _control_point_toggled_on_press = true;
353 } else if (!press && !_control_point_toggled_on_press) {
354 /* This is the release, and the point wasn't toggled on the press, so do it now */
355 selection->toggle (clicked_control_point);
359 _control_point_toggled_on_press = false;
362 case Selection::Extend:
371 Editor::get_onscreen_tracks (TrackViewList& tvl)
373 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
374 if ((*i)->y_position() < _visible_canvas_height) {
380 /** Call a slot for a given `basis' track and also for any track that is in the same
381 * active route group with a particular set of properties.
383 * @param sl Slot to call.
384 * @param basis Basis track.
385 * @param prop Properties that active edit groups must share to be included in the map.
389 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
391 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
393 if (route_basis == 0) {
397 set<RouteTimeAxisView*> tracks;
398 tracks.insert (route_basis);
400 RouteGroup* group = route_basis->route()->route_group();
402 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
404 /* the basis is a member of an active route group, with the appropriate
405 properties; find other members */
407 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
408 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
409 if (v && v->route()->route_group() == group) {
416 uint32_t const sz = tracks.size ();
418 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
423 /** Call a slot for a given `basis' track and also for any track that is in the same
424 * active route group with a particular set of properties.
426 * @param sl Slot to call.
427 * @param basis Basis track.
428 * @param prop Properties that active edit groups must share to be included in the map.
432 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
434 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
435 set<boost::shared_ptr<Playlist> > playlists;
437 if (route_basis == 0) {
441 set<RouteTimeAxisView*> tracks;
442 tracks.insert (route_basis);
444 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
446 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
448 /* the basis is a member of an active route group, with the appropriate
449 properties; find other members */
451 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
452 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
454 if (v && v->route()->route_group() == group) {
456 boost::shared_ptr<Track> t = v->track();
458 if (playlists.insert (t->playlist()).second) {
459 /* haven't seen this playlist yet */
463 /* not actually a "Track", but a timeaxis view that
464 we should mapover anyway.
473 uint32_t const sz = tracks.size ();
475 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
481 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
483 boost::shared_ptr<Playlist> pl;
484 vector<boost::shared_ptr<Region> > results;
486 boost::shared_ptr<Track> tr;
488 if ((tr = tv.track()) == 0) {
493 if (&tv == &basis->get_time_axis_view()) {
494 /* looking in same track as the original */
498 if ((pl = tr->playlist()) != 0) {
499 pl->get_equivalent_regions (basis->region(), results);
502 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
503 if ((marv = tv.view()->find_view (*ir)) != 0) {
504 all_equivs->push_back (marv);
510 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
512 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);
514 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
516 equivalent_regions.push_back (basis);
520 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
522 RegionSelection equivalent;
524 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
526 vector<RegionView*> eq;
528 mapover_tracks_with_unique_playlists (
529 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
530 &(*i)->get_time_axis_view(), prop);
532 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
543 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
545 int region_count = 0;
547 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 RouteTimeAxisView* tatv;
551 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
553 boost::shared_ptr<Playlist> pl;
554 vector<boost::shared_ptr<Region> > results;
556 boost::shared_ptr<Track> tr;
558 if ((tr = tatv->track()) == 0) {
563 if ((pl = (tr->playlist())) != 0) {
564 pl->get_region_list_equivalent_regions (region, results);
567 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
568 if ((marv = tatv->view()->find_view (*ir)) != 0) {
581 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
583 vector<RegionView*> all_equivalent_regions;
586 if (!clicked_regionview || !clicked_routeview) {
591 button_release_can_deselect = false;
594 if (op == Selection::Toggle || op == Selection::Set) {
597 case Selection::Toggle:
598 if (selection->selected (clicked_regionview)) {
601 /* whatever was clicked was selected already; do nothing here but allow
602 the button release to deselect it
605 button_release_can_deselect = true;
608 if (button_release_can_deselect) {
610 /* just remove this one region, but only on a permitted button release */
612 selection->remove (clicked_regionview);
615 /* no more deselect action on button release till a new press
616 finds an already selected object.
619 button_release_can_deselect = false;
627 if (selection->selected (clicked_routeview)) {
628 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
630 all_equivalent_regions.push_back (clicked_regionview);
633 /* add all the equivalent regions, but only on button press */
635 if (!all_equivalent_regions.empty()) {
639 selection->add (all_equivalent_regions);
645 if (!selection->selected (clicked_regionview)) {
646 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
647 selection->set (all_equivalent_regions);
650 /* clicked on an already selected region */
654 if (selection->regions.size() > 1) {
655 /* collapse region selection down to just this one region (and its equivalents) */
656 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
657 selection->set(all_equivalent_regions);
669 } else if (op == Selection::Extend) {
671 list<Selectable*> results;
672 samplepos_t last_sample;
673 samplepos_t first_sample;
674 bool same_track = false;
676 /* 1. find the last selected regionview in the track that was clicked in */
679 first_sample = max_samplepos;
681 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
682 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
684 if ((*x)->region()->last_sample() > last_sample) {
685 last_sample = (*x)->region()->last_sample();
688 if ((*x)->region()->first_sample() < first_sample) {
689 first_sample = (*x)->region()->first_sample();
698 /* 2. figure out the boundaries for our search for new objects */
700 switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
701 case Evoral::OverlapNone:
702 if (last_sample < clicked_regionview->region()->first_sample()) {
703 first_sample = last_sample;
704 last_sample = clicked_regionview->region()->last_sample();
706 last_sample = first_sample;
707 first_sample = clicked_regionview->region()->first_sample();
711 case Evoral::OverlapExternal:
712 if (last_sample < clicked_regionview->region()->first_sample()) {
713 first_sample = last_sample;
714 last_sample = clicked_regionview->region()->last_sample();
716 last_sample = first_sample;
717 first_sample = clicked_regionview->region()->first_sample();
721 case Evoral::OverlapInternal:
722 if (last_sample < clicked_regionview->region()->first_sample()) {
723 first_sample = last_sample;
724 last_sample = clicked_regionview->region()->last_sample();
726 last_sample = first_sample;
727 first_sample = clicked_regionview->region()->first_sample();
731 case Evoral::OverlapStart:
732 case Evoral::OverlapEnd:
733 /* nothing to do except add clicked region to selection, since it
734 overlaps with the existing selection in this track.
741 /* click in a track that has no regions selected, so extend vertically
742 to pick out all regions that are defined by the existing selection
747 first_sample = clicked_regionview->region()->position();
748 last_sample = clicked_regionview->region()->last_sample();
750 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
751 if ((*i)->region()->position() < first_sample) {
752 first_sample = (*i)->region()->position();
754 if ((*i)->region()->last_sample() + 1 > last_sample) {
755 last_sample = (*i)->region()->last_sample();
760 /* 2. find all the tracks we should select in */
762 set<RouteTimeAxisView*> relevant_tracks;
764 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
765 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
767 relevant_tracks.insert (r);
771 set<RouteTimeAxisView*> already_in_selection;
773 if (relevant_tracks.empty()) {
775 /* no tracks selected .. thus .. if the
776 regionview we're in isn't selected
777 (i.e. we're about to extend to it), then
778 find all tracks between the this one and
782 if (!selection->selected (clicked_regionview)) {
784 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
788 /* add this track to the ones we will search */
790 relevant_tracks.insert (rtv);
792 /* find the track closest to this one that
793 already a selected region.
796 RouteTimeAxisView* closest = 0;
797 int distance = INT_MAX;
798 int key = rtv->route()->presentation_info().order ();
800 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
802 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
804 if (artv && artv != rtv) {
806 pair<set<RouteTimeAxisView*>::iterator,bool> result;
808 result = already_in_selection.insert (artv);
811 /* newly added to already_in_selection */
813 int d = artv->route()->presentation_info().order ();
817 if (abs (d) < distance) {
827 /* now add all tracks between that one and this one */
829 int okey = closest->route()->presentation_info().order ();
835 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
836 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
837 if (artv && artv != rtv) {
839 int k = artv->route()->presentation_info().order ();
841 if (k >= okey && k <= key) {
843 /* in range but don't add it if
844 it already has tracks selected.
845 this avoids odd selection
846 behaviour that feels wrong.
849 if (find (already_in_selection.begin(),
850 already_in_selection.end(),
851 artv) == already_in_selection.end()) {
853 relevant_tracks.insert (artv);
863 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
864 one that was clicked.
867 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
868 (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
871 /* 4. convert to a vector of regions */
873 vector<RegionView*> regions;
875 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
878 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
879 regions.push_back (arv);
883 if (!regions.empty()) {
884 selection->add (regions);
886 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
887 /* ensure that at least the clicked regionview is selected. */
888 selection->set (clicked_regionview);
899 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
904 begin_reversible_selection_op (X_("set selection"));
906 case Selection::Toggle:
907 selection->toggle (s);
912 case Selection::Extend:
920 commit_reversible_selection_op () ;
924 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
926 vector<RegionView*> all_equivalent_regions;
928 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
930 if (all_equivalent_regions.empty()) {
934 begin_reversible_selection_op (X_("set selected regions"));
937 case Selection::Toggle:
938 /* XXX this is not correct */
939 selection->toggle (all_equivalent_regions);
942 selection->set (all_equivalent_regions);
944 case Selection::Extend:
945 selection->add (all_equivalent_regions);
948 selection->add (all_equivalent_regions);
952 commit_reversible_selection_op () ;
956 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
959 boost::shared_ptr<Region> r (weak_r.lock());
965 if ((rv = sv->find_view (r)) == 0) {
969 /* don't reset the selection if its something other than
970 a single other region.
973 if (selection->regions.size() > 1) {
977 begin_reversible_selection_op (X_("set selected regions"));
981 commit_reversible_selection_op () ;
986 struct SelectionOrderSorter {
987 bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const {
988 boost::shared_ptr<Stripable> sa = a->stripable ();
989 boost::shared_ptr<Stripable> sb = b->stripable ();
999 return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
1004 Editor::presentation_info_changed (PropertyChange const & what_changed)
1006 uint32_t n_tracks = 0;
1007 uint32_t n_busses = 0;
1008 uint32_t n_vcas = 0;
1009 uint32_t n_routes = 0;
1010 uint32_t n_stripables = 0;
1012 /* We cannot ensure ordering of the handlers for
1013 * PresentationInfo::Changed, so we have to do everything in order
1014 * here, as a single handler.
1017 if (what_changed.contains (Properties::selected)) {
1018 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1019 (*i)->set_selected (false);
1020 (*i)->hide_selection ();
1024 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1025 * currently selected stripable/controllable duples are found and added
1028 selection->core_selection_changed (what_changed);
1030 /* STEP 2: update TimeAxisView's knowledge of their selected state
1033 if (what_changed.contains (Properties::selected)) {
1035 StripableNotificationListPtr stripables (new StripableNotificationList);
1037 switch (selection->tracks.size()) {
1041 set_selected_mixer_strip (*(selection->tracks.back()));
1042 if (!_track_selection_change_without_scroll && !_editor_track_selection_change_without_scroll) {
1043 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1048 CoreSelection::StripableAutomationControls sc;
1049 _session->selection().get_stripables (sc);
1051 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1053 AxisView* av = axis_view_by_stripable ((*i).stripable);
1061 if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1064 } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1067 } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1071 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1075 continue; /* impossible */
1078 if (!(*i).controllable) {
1080 /* "parent" track selected */
1081 tav->set_selected (true);
1082 tav->reshow_selection (selection->time);
1086 /* possibly a child */
1088 TimeAxisView::Children c = tav->get_child_list ();
1090 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1092 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1094 if (control != (*i).controllable) {
1098 (*j)->set_selected (true);
1099 (*j)->reshow_selection (selection->time);
1103 stripables->push_back ((*i).stripable);
1106 ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1107 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1108 ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1109 ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1110 ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1112 sensitize_the_right_region_actions (false);
1114 /* STEP 4: notify control protocols */
1116 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1118 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1119 uint32_t audio_track_cnt = 0;
1120 uint32_t midi_track_cnt = 0;
1122 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1123 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1126 if (atv->is_audio_track()) {
1131 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1134 if (mtv->is_midi_track()) {
1141 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1145 /* STEP 4: update EditorRoutes treeview */
1149 soh.add (Properties::selected);
1150 soh.add (Properties::order);
1151 soh.add (Properties::hidden);
1153 if (what_changed.contains (soh)) {
1154 _routes->sync_treeview_from_presentation_info (what_changed);
1159 Editor::track_selection_changed ()
1161 /* reset paste count, so the plaste location doesn't get incremented
1162 * if we want to paste in the same place, but different track. */
1165 if ( _session->solo_selection_active() )
1166 play_solo_selection(false);
1170 Editor::time_selection_changed ()
1172 /* XXX this is superficially inefficient. Hide the selection in all
1173 * tracks, then show it in all selected tracks.
1175 * However, if you investigate what this actually does, it isn't
1176 * anywhere nearly as bad as it may appear. Remember: nothing is
1177 * redrawn or even recomputed during these two loops - that only
1178 * happens when we next render ...
1181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1182 (*i)->hide_selection ();
1185 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1186 (*i)->show_selection (selection->time);
1189 if (selection->time.empty()) {
1190 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1192 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1195 /* propagate into backend, but only when there is no drag or we are at
1196 * the end of a drag, otherwise this is too expensive (could case a
1197 * locate per mouse motion event.
1200 if (_session && !_drags->active()) {
1201 if (selection->time.length() != 0) {
1202 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1204 _session->clear_range_selection ();
1209 /** Set all region actions to have a given sensitivity */
1211 Editor::sensitize_all_region_actions (bool s)
1213 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1215 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1216 (*i)->set_sensitive (s);
1219 _all_region_actions_sensitized = s;
1222 /** Sensitize region-based actions.
1224 * This method is called from whenever we leave the canvas, either by moving
1225 * the pointer out of it, or by popping up a context menu. See
1226 * Editor::{entered,left}_track_canvas() for details there.
1229 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1231 bool have_selection = false;
1232 bool have_entered = false;
1233 bool have_edit_point = false;
1236 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1239 if (!selection->regions.empty()) {
1240 have_selection = true;
1241 rs = selection->regions;
1244 if (entered_regionview) {
1245 have_entered = true;
1246 rs.add (entered_regionview);
1249 if (rs.empty() && !selection->tracks.empty()) {
1251 /* no selected regions, but some selected tracks.
1254 if (_edit_point == EditAtMouse) {
1255 if (!within_track_canvas) {
1256 /* pointer is not in canvas, so edit point is meaningless */
1257 have_edit_point = false;
1259 /* inside canvas. we don't know where the edit
1260 point will be when an action is invoked, but
1261 assume it could intersect with a region.
1263 have_edit_point = true;
1266 RegionSelection at_edit_point;
1267 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1268 get_regions_at (at_edit_point, where, selection->tracks);
1269 if (!at_edit_point.empty()) {
1270 have_edit_point = true;
1273 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1278 //std::cerr << "\tfinal have selection: " << have_selection
1279 // << " have entered " << have_entered
1280 // << " have edit point " << have_edit_point
1281 // << " EP = " << enum_2_string (_edit_point)
1284 typedef std::map<std::string,RegionAction> RegionActionMap;
1286 _ignore_region_action = true;
1288 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1289 RegionActionTarget tgt = x->second.target;
1290 bool sensitive = false;
1292 if ((tgt & SelectedRegions) && have_selection) {
1294 } else if ((tgt & EnteredRegions) && have_entered) {
1296 } else if ((tgt & EditPointRegions) && have_edit_point) {
1300 x->second.action->set_sensitive (sensitive);
1303 /* Look through the regions that are selected and make notes about what we have got */
1305 bool have_audio = false;
1306 bool have_multichannel_audio = false;
1307 bool have_midi = false;
1308 bool have_locked = false;
1309 bool have_unlocked = false;
1310 bool have_video_locked = false;
1311 bool have_video_unlocked = false;
1312 bool have_position_lock_style_audio = false;
1313 bool have_position_lock_style_music = false;
1314 bool have_muted = false;
1315 bool have_unmuted = false;
1316 bool have_opaque = false;
1317 bool have_non_opaque = false;
1318 bool have_not_at_natural_position = false;
1319 bool have_envelope_active = false;
1320 bool have_envelope_inactive = false;
1321 bool have_non_unity_scale_amplitude = false;
1322 bool have_compound_regions = false;
1323 bool have_inactive_fade_in = false;
1324 bool have_inactive_fade_out = false;
1325 bool have_active_fade_in = false;
1326 bool have_active_fade_out = false;
1327 bool have_transients = false;
1329 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1331 boost::shared_ptr<Region> r = (*i)->region ();
1332 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1336 if (ar->n_channels() > 1) {
1337 have_multichannel_audio = true;
1341 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1345 if (r->is_compound()) {
1346 have_compound_regions = true;
1352 have_unlocked = true;
1355 if (r->video_locked()) {
1356 have_video_locked = true;
1358 have_video_unlocked = true;
1361 if (r->position_lock_style() == MusicTime) {
1362 have_position_lock_style_music = true;
1364 have_position_lock_style_audio = true;
1370 have_unmuted = true;
1376 have_non_opaque = true;
1379 if (!r->at_natural_position()) {
1380 have_not_at_natural_position = true;
1383 if (r->has_transients ()){
1384 have_transients = true;
1388 if (ar->envelope_active()) {
1389 have_envelope_active = true;
1391 have_envelope_inactive = true;
1394 if (ar->scale_amplitude() != 1) {
1395 have_non_unity_scale_amplitude = true;
1398 if (ar->fade_in_active ()) {
1399 have_active_fade_in = true;
1401 have_inactive_fade_in = true;
1404 if (ar->fade_out_active ()) {
1405 have_active_fade_out = true;
1407 have_inactive_fade_out = true;
1412 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1414 if (rs.size() > 1) {
1415 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1416 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1417 _region_actions->get_action("rename-region")->set_sensitive (false);
1419 /* XXX need to check whether there is than 1 per
1420 playlist, because otherwise this makes no sense.
1422 _region_actions->get_action("combine-regions")->set_sensitive (true);
1424 _region_actions->get_action("combine-regions")->set_sensitive (false);
1426 } else if (rs.size() == 1) {
1427 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1428 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1429 _region_actions->get_action("combine-regions")->set_sensitive (false);
1432 if (!have_multichannel_audio) {
1433 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1437 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1438 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1439 _region_actions->get_action("quantize-region")->set_sensitive (false);
1440 _region_actions->get_action("legatize-region")->set_sensitive (false);
1441 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1442 _region_actions->get_action("transform-region")->set_sensitive (false);
1443 _region_actions->get_action("fork-region")->set_sensitive (false);
1444 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1445 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1446 _region_actions->get_action("transpose-region")->set_sensitive (false);
1448 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1449 /* others were already marked sensitive */
1452 /* ok, moving along... */
1454 if (have_compound_regions) {
1455 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1457 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1462 if (have_envelope_active && !have_envelope_inactive) {
1463 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1464 } else if (have_envelope_active && have_envelope_inactive) {
1465 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1470 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1471 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1472 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1473 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1474 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1475 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1476 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1480 if (!have_non_unity_scale_amplitude || !have_audio) {
1481 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1484 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1485 a->set_active (have_locked && !have_unlocked);
1486 if (have_locked && have_unlocked) {
1487 // a->set_inconsistent ();
1490 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1491 a->set_active (have_video_locked && !have_video_unlocked);
1492 if (have_video_locked && have_video_unlocked) {
1493 // a->set_inconsistent ();
1496 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1497 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1499 vector<Widget*> proxies = a->get_proxies();
1500 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1501 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1503 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1507 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1508 a->set_active (have_muted && !have_unmuted);
1509 if (have_muted && have_unmuted) {
1510 // a->set_inconsistent ();
1513 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1514 a->set_active (have_opaque && !have_non_opaque);
1515 if (have_opaque && have_non_opaque) {
1516 // a->set_inconsistent ();
1519 if (!have_not_at_natural_position) {
1520 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1523 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1524 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1525 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1527 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1530 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1531 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1532 if (have_active_fade_in && have_inactive_fade_in) {
1533 // a->set_inconsistent ();
1536 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1537 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1539 if (have_active_fade_out && have_inactive_fade_out) {
1540 // a->set_inconsistent ();
1543 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1544 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1546 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1547 a->set_active (have_active_fade && !have_inactive_fade);
1549 if (have_active_fade && have_inactive_fade) {
1550 // a->set_inconsistent ();
1553 _ignore_region_action = false;
1555 _all_region_actions_sensitized = false;
1559 Editor::region_selection_changed ()
1561 _regions->block_change_connection (true);
1562 editor_regions_selection_changed_connection.block(true);
1564 if (_region_selection_change_updates_region_list) {
1565 _regions->unselect_all ();
1568 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1569 (*i)->set_selected_regionviews (selection->regions);
1572 if (_region_selection_change_updates_region_list) {
1573 _regions->set_selected (selection->regions);
1576 _regions->block_change_connection (false);
1577 editor_regions_selection_changed_connection.block(false);
1579 sensitize_the_right_region_actions (false);
1581 /* propagate into backend */
1584 if (!selection->regions.empty()) {
1585 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1587 _session->clear_object_selection ();
1591 if ( _session->solo_selection_active() )
1592 play_solo_selection(false);
1596 Editor::point_selection_changed ()
1598 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1599 (*i)->set_selected_points (selection->points);
1604 Editor::select_all_in_track (Selection::Operation op)
1606 list<Selectable *> touched;
1608 if (!clicked_routeview) {
1612 begin_reversible_selection_op (X_("Select All in Track"));
1614 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1617 case Selection::Toggle:
1618 selection->add (touched);
1620 case Selection::Set:
1621 selection->set (touched);
1623 case Selection::Extend:
1624 /* meaningless, because we're selecting everything */
1626 case Selection::Add:
1627 selection->add (touched);
1631 commit_reversible_selection_op ();
1635 Editor::select_all_internal_edit (Selection::Operation)
1637 bool selected = false;
1639 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1640 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1642 mrv->select_all_notes ();
1647 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1649 mrv->select_all_notes ();
1657 Editor::select_all_objects (Selection::Operation op)
1659 list<Selectable *> touched;
1661 if (internal_editing() && select_all_internal_edit(op)) {
1662 return; // Selected notes
1667 if (selection->tracks.empty()) {
1670 ts = selection->tracks;
1673 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1674 if ((*iter)->hidden()) {
1677 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1680 begin_reversible_selection_op (X_("select all"));
1682 case Selection::Add:
1683 selection->add (touched);
1685 case Selection::Toggle:
1686 selection->toggle (touched);
1688 case Selection::Set:
1689 selection->set (touched);
1691 case Selection::Extend:
1692 /* meaningless, because we're selecting everything */
1695 commit_reversible_selection_op ();
1699 Editor::invert_selection_in_track ()
1701 list<Selectable *> touched;
1703 if (!clicked_routeview) {
1707 begin_reversible_selection_op (X_("Invert Selection in Track"));
1708 clicked_routeview->get_inverted_selectables (*selection, touched);
1709 selection->set (touched);
1710 commit_reversible_selection_op ();
1714 Editor::invert_selection ()
1717 if (internal_editing()) {
1718 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1719 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1721 mrv->invert_selection ();
1727 if (!selection->tracks.empty()) {
1729 TrackViewList inverted;
1731 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1732 if (!(*iter)->selected()) {
1733 inverted.push_back (*iter);
1737 begin_reversible_selection_op (X_("Invert Track Selection"));
1738 selection->set (inverted);
1739 commit_reversible_selection_op ();
1743 list<Selectable *> touched;
1745 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1746 if ((*iter)->hidden()) {
1749 (*iter)->get_inverted_selectables (*selection, touched);
1752 begin_reversible_selection_op (X_("Invert ObjectSelection"));
1753 selection->set (touched);
1754 commit_reversible_selection_op ();
1758 /** @param start Start time in session samples.
1759 * @param end End time in session samples.
1760 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1761 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1762 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1763 * within the region are already selected.
1766 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1768 list<Selectable*> found;
1770 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1772 if ((*iter)->hidden()) {
1776 (*iter)->get_selectables (start, end, top, bot, found);
1779 if (found.empty()) {
1780 selection->clear_objects();
1781 selection->clear_time ();
1785 if (preserve_if_selected && op != Selection::Toggle) {
1786 list<Selectable*>::iterator i = found.begin();
1787 while (i != found.end() && (*i)->selected()) {
1791 if (i == found.end()) {
1796 begin_reversible_selection_op (X_("select all within"));
1798 case Selection::Add:
1799 selection->add (found);
1801 case Selection::Toggle:
1802 selection->toggle (found);
1804 case Selection::Set:
1805 selection->set (found);
1807 case Selection::Extend:
1808 /* not defined yet */
1812 commit_reversible_selection_op ();
1816 Editor::set_selection_from_region ()
1818 if (selection->regions.empty()) {
1822 /* find all the tracks that have selected regions */
1824 set<TimeAxisView*> tracks;
1826 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1827 tracks.insert (&(*r)->get_time_axis_view());
1831 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1833 /* select range (this will clear the region selection) */
1835 selection->set (selection->regions.start(), selection->regions.end_sample());
1837 /* and select the tracks */
1839 selection->set (tvl);
1841 if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1842 set_mouse_mode (Editing::MouseRange, false);
1847 Editor::set_selection_from_punch()
1851 if ((location = _session->locations()->auto_punch_location()) == 0) {
1855 set_selection_from_range (*location);
1859 Editor::set_selection_from_loop()
1863 if ((location = _session->locations()->auto_loop_location()) == 0) {
1866 set_selection_from_range (*location);
1870 Editor::set_selection_from_range (Location& loc)
1872 begin_reversible_selection_op (X_("set selection from range"));
1874 selection->set (loc.start(), loc.end());
1876 // if no tracks are selected, enable all tracks
1877 // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1878 if (selection->tracks.empty()) {
1879 select_all_tracks();
1882 commit_reversible_selection_op ();
1884 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1885 set_mouse_mode (MouseRange, false);
1890 Editor::select_all_selectables_using_time_selection ()
1892 list<Selectable *> touched;
1894 if (selection->time.empty()) {
1898 samplepos_t start = selection->time[clicked_selection].start;
1899 samplepos_t end = selection->time[clicked_selection].end;
1901 if (end - start < 1) {
1907 if (selection->tracks.empty()) {
1910 ts = &selection->tracks;
1913 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1914 if ((*iter)->hidden()) {
1917 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1920 begin_reversible_selection_op (X_("select all from range"));
1921 selection->set (touched);
1922 commit_reversible_selection_op ();
1927 Editor::select_all_selectables_using_punch()
1929 Location* location = _session->locations()->auto_punch_location();
1930 list<Selectable *> touched;
1932 if (location == 0 || (location->end() - location->start() <= 1)) {
1939 if (selection->tracks.empty()) {
1942 ts = &selection->tracks;
1945 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1946 if ((*iter)->hidden()) {
1949 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1951 begin_reversible_selection_op (X_("select all from punch"));
1952 selection->set (touched);
1953 commit_reversible_selection_op ();
1958 Editor::select_all_selectables_using_loop()
1960 Location* location = _session->locations()->auto_loop_location();
1961 list<Selectable *> touched;
1963 if (location == 0 || (location->end() - location->start() <= 1)) {
1970 if (selection->tracks.empty()) {
1973 ts = &selection->tracks;
1976 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1977 if ((*iter)->hidden()) {
1980 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1982 begin_reversible_selection_op (X_("select all from loop"));
1983 selection->set (touched);
1984 commit_reversible_selection_op ();
1989 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1993 list<Selectable *> touched;
1996 start = cursor->current_sample();
1997 end = _session->current_end_sample();
1999 if (cursor->current_sample() > 0) {
2001 end = cursor->current_sample() - 1;
2007 if (internal_editing()) {
2008 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2009 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2011 mrv->select_range (start, end);
2018 begin_reversible_selection_op (X_("select all after cursor"));
2020 begin_reversible_selection_op (X_("select all before cursor"));
2025 if (selection->tracks.empty()) {
2028 ts = &selection->tracks;
2031 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2032 if ((*iter)->hidden()) {
2035 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2037 selection->set (touched);
2038 commit_reversible_selection_op ();
2042 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2046 list<Selectable *> touched;
2049 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2050 end = _session->current_end_sample();
2052 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2060 if (internal_editing()) {
2061 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2062 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2063 mrv->select_range (start, end);
2069 begin_reversible_selection_op (X_("select all after edit"));
2071 begin_reversible_selection_op (X_("select all before edit"));
2076 if (selection->tracks.empty()) {
2079 ts = &selection->tracks;
2082 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2083 if ((*iter)->hidden()) {
2086 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2088 selection->set (touched);
2089 commit_reversible_selection_op ();
2093 Editor::select_all_selectables_between (bool within)
2097 list<Selectable *> touched;
2099 if (!get_edit_op_range (start, end)) {
2103 if (internal_editing()) {
2104 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2105 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2106 mrv->select_range (start, end);
2113 if (selection->tracks.empty()) {
2116 ts = &selection->tracks;
2119 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2120 if ((*iter)->hidden()) {
2123 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2126 begin_reversible_selection_op (X_("Select all Selectables Between"));
2127 selection->set (touched);
2128 commit_reversible_selection_op ();
2132 Editor::select_range_between ()
2137 if (!selection->time.empty()) {
2138 selection->clear_time ();
2141 if (!get_edit_op_range (start, end)) {
2145 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2146 set_mouse_mode (MouseRange, false);
2149 begin_reversible_selection_op (X_("Select Range Between"));
2150 selection->set (start, end);
2151 commit_reversible_selection_op ();
2155 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2160 /* if an explicit range exists, use it */
2162 if ((mouse_mode == MouseRange || get_smart_mode()) && !selection->time.empty()) {
2163 /* we know that these are ordered */
2164 start = selection->time.start();
2165 end = selection->time.end_sample();
2173 // if (!mouse_sample (m, ignored)) {
2174 // /* mouse is not in a canvas, try playhead+selected marker.
2175 // this is probably most true when using menus.
2178 // if (selection->markers.empty()) {
2182 // start = selection->markers.front()->position();
2183 // end = _session->audible_sample();
2187 // switch (_edit_point) {
2188 // case EditAtPlayhead:
2189 // if (selection->markers.empty()) {
2190 // /* use mouse + playhead */
2192 // end = _session->audible_sample();
2194 // /* use playhead + selected marker */
2195 // start = _session->audible_sample();
2196 // end = selection->markers.front()->position();
2200 // case EditAtMouse:
2201 // /* use mouse + selected marker */
2202 // if (selection->markers.empty()) {
2204 // end = _session->audible_sample();
2206 // start = selection->markers.front()->position();
2211 // case EditAtSelectedMarker:
2212 // /* use mouse + selected marker */
2213 // if (selection->markers.empty()) {
2215 // MessageDialog win (_("No edit range defined"),
2220 // win.set_secondary_text (
2221 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2224 // win.set_default_response (RESPONSE_CLOSE);
2225 // win.set_position (Gtk::WIN_POS_MOUSE);
2230 // return false; // NO RANGE
2232 // start = selection->markers.front()->position();
2238 // if (start == end) {
2242 // if (start > end) {
2243 // swap (start, end);
2246 /* turn range into one delimited by start...end,
2256 Editor::deselect_all ()
2258 begin_reversible_selection_op (X_("Deselect All"));
2259 selection->clear ();
2260 commit_reversible_selection_op ();
2264 Editor::select_range (samplepos_t s, samplepos_t e)
2266 begin_reversible_selection_op (X_("Select Range"));
2267 selection->add (clicked_axisview);
2268 selection->time.clear ();
2269 long ret = selection->set (s, e);
2270 commit_reversible_selection_op ();