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 if ( _session->solo_selection_active() )
1162 play_solo_selection(false);
1166 Editor::time_selection_changed ()
1168 /* XXX this is superficially inefficient. Hide the selection in all
1169 * tracks, then show it in all selected tracks.
1171 * However, if you investigate what this actually does, it isn't
1172 * anywhere nearly as bad as it may appear. Remember: nothing is
1173 * redrawn or even recomputed during these two loops - that only
1174 * happens when we next render ...
1177 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1178 (*i)->hide_selection ();
1181 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1182 (*i)->show_selection (selection->time);
1185 if (selection->time.empty()) {
1186 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1188 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1191 /* propagate into backend, but only when there is no drag or we are at
1192 * the end of a drag, otherwise this is too expensive (could case a
1193 * locate per mouse motion event.
1196 if (_session && !_drags->active()) {
1197 if (selection->time.length() != 0) {
1198 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1200 _session->clear_range_selection ();
1205 /** Set all region actions to have a given sensitivity */
1207 Editor::sensitize_all_region_actions (bool s)
1209 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1211 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1212 (*i)->set_sensitive (s);
1215 _all_region_actions_sensitized = s;
1218 /** Sensitize region-based actions.
1220 * This method is called from whenever we leave the canvas, either by moving
1221 * the pointer out of it, or by popping up a context menu. See
1222 * Editor::{entered,left}_track_canvas() for details there.
1225 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1227 bool have_selection = false;
1228 bool have_entered = false;
1229 bool have_edit_point = false;
1232 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1235 if (!selection->regions.empty()) {
1236 have_selection = true;
1237 rs = selection->regions;
1240 if (entered_regionview) {
1241 have_entered = true;
1242 rs.add (entered_regionview);
1245 if (rs.empty() && !selection->tracks.empty()) {
1247 /* no selected regions, but some selected tracks.
1250 if (_edit_point == EditAtMouse) {
1251 if (!within_track_canvas) {
1252 /* pointer is not in canvas, so edit point is meaningless */
1253 have_edit_point = false;
1255 /* inside canvas. we don't know where the edit
1256 point will be when an action is invoked, but
1257 assume it could intersect with a region.
1259 have_edit_point = true;
1262 RegionSelection at_edit_point;
1263 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1264 get_regions_at (at_edit_point, where, selection->tracks);
1265 if (!at_edit_point.empty()) {
1266 have_edit_point = true;
1269 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1274 //std::cerr << "\tfinal have selection: " << have_selection
1275 // << " have entered " << have_entered
1276 // << " have edit point " << have_edit_point
1277 // << " EP = " << enum_2_string (_edit_point)
1280 typedef std::map<std::string,RegionAction> RegionActionMap;
1282 _ignore_region_action = true;
1284 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1285 RegionActionTarget tgt = x->second.target;
1286 bool sensitive = false;
1288 if ((tgt & SelectedRegions) && have_selection) {
1290 } else if ((tgt & EnteredRegions) && have_entered) {
1292 } else if ((tgt & EditPointRegions) && have_edit_point) {
1296 x->second.action->set_sensitive (sensitive);
1299 /* Look through the regions that are selected and make notes about what we have got */
1301 bool have_audio = false;
1302 bool have_multichannel_audio = false;
1303 bool have_midi = false;
1304 bool have_locked = false;
1305 bool have_unlocked = false;
1306 bool have_video_locked = false;
1307 bool have_video_unlocked = false;
1308 bool have_position_lock_style_audio = false;
1309 bool have_position_lock_style_music = false;
1310 bool have_muted = false;
1311 bool have_unmuted = false;
1312 bool have_opaque = false;
1313 bool have_non_opaque = false;
1314 bool have_not_at_natural_position = false;
1315 bool have_envelope_active = false;
1316 bool have_envelope_inactive = false;
1317 bool have_non_unity_scale_amplitude = false;
1318 bool have_compound_regions = false;
1319 bool have_inactive_fade_in = false;
1320 bool have_inactive_fade_out = false;
1321 bool have_active_fade_in = false;
1322 bool have_active_fade_out = false;
1323 bool have_transients = false;
1325 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1327 boost::shared_ptr<Region> r = (*i)->region ();
1328 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1332 if (ar->n_channels() > 1) {
1333 have_multichannel_audio = true;
1337 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1341 if (r->is_compound()) {
1342 have_compound_regions = true;
1348 have_unlocked = true;
1351 if (r->video_locked()) {
1352 have_video_locked = true;
1354 have_video_unlocked = true;
1357 if (r->position_lock_style() == MusicTime) {
1358 have_position_lock_style_music = true;
1360 have_position_lock_style_audio = true;
1366 have_unmuted = true;
1372 have_non_opaque = true;
1375 if (!r->at_natural_position()) {
1376 have_not_at_natural_position = true;
1379 if (r->has_transients ()){
1380 have_transients = true;
1384 if (ar->envelope_active()) {
1385 have_envelope_active = true;
1387 have_envelope_inactive = true;
1390 if (ar->scale_amplitude() != 1) {
1391 have_non_unity_scale_amplitude = true;
1394 if (ar->fade_in_active ()) {
1395 have_active_fade_in = true;
1397 have_inactive_fade_in = true;
1400 if (ar->fade_out_active ()) {
1401 have_active_fade_out = true;
1403 have_inactive_fade_out = true;
1408 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1410 if (rs.size() > 1) {
1411 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1412 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1413 _region_actions->get_action("rename-region")->set_sensitive (false);
1415 /* XXX need to check whether there is than 1 per
1416 playlist, because otherwise this makes no sense.
1418 _region_actions->get_action("combine-regions")->set_sensitive (true);
1420 _region_actions->get_action("combine-regions")->set_sensitive (false);
1422 } else if (rs.size() == 1) {
1423 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1424 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1425 _region_actions->get_action("combine-regions")->set_sensitive (false);
1428 if (!have_multichannel_audio) {
1429 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1433 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1434 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1435 _region_actions->get_action("quantize-region")->set_sensitive (false);
1436 _region_actions->get_action("legatize-region")->set_sensitive (false);
1437 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1438 _region_actions->get_action("transform-region")->set_sensitive (false);
1439 _region_actions->get_action("fork-region")->set_sensitive (false);
1440 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1441 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1442 _region_actions->get_action("transpose-region")->set_sensitive (false);
1444 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1445 /* others were already marked sensitive */
1448 /* ok, moving along... */
1450 if (have_compound_regions) {
1451 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1453 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1458 if (have_envelope_active && !have_envelope_inactive) {
1459 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1460 } else if (have_envelope_active && have_envelope_inactive) {
1461 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1466 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1467 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1468 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1469 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1470 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1471 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1472 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1476 if (!have_non_unity_scale_amplitude || !have_audio) {
1477 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1480 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1481 a->set_active (have_locked && !have_unlocked);
1482 if (have_locked && have_unlocked) {
1483 // a->set_inconsistent ();
1486 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1487 a->set_active (have_video_locked && !have_video_unlocked);
1488 if (have_video_locked && have_video_unlocked) {
1489 // a->set_inconsistent ();
1492 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1493 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1495 vector<Widget*> proxies = a->get_proxies();
1496 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1497 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1499 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1503 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1504 a->set_active (have_muted && !have_unmuted);
1505 if (have_muted && have_unmuted) {
1506 // a->set_inconsistent ();
1509 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1510 a->set_active (have_opaque && !have_non_opaque);
1511 if (have_opaque && have_non_opaque) {
1512 // a->set_inconsistent ();
1515 if (!have_not_at_natural_position) {
1516 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1519 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1520 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1521 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1523 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1526 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1527 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1528 if (have_active_fade_in && have_inactive_fade_in) {
1529 // a->set_inconsistent ();
1532 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1533 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1535 if (have_active_fade_out && have_inactive_fade_out) {
1536 // a->set_inconsistent ();
1539 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1540 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1542 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1543 a->set_active (have_active_fade && !have_inactive_fade);
1545 if (have_active_fade && have_inactive_fade) {
1546 // a->set_inconsistent ();
1549 _ignore_region_action = false;
1551 _all_region_actions_sensitized = false;
1555 Editor::region_selection_changed ()
1557 _regions->block_change_connection (true);
1558 editor_regions_selection_changed_connection.block(true);
1560 if (_region_selection_change_updates_region_list) {
1561 _regions->unselect_all ();
1564 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1565 (*i)->set_selected_regionviews (selection->regions);
1568 if (_region_selection_change_updates_region_list) {
1569 _regions->set_selected (selection->regions);
1572 _regions->block_change_connection (false);
1573 editor_regions_selection_changed_connection.block(false);
1575 sensitize_the_right_region_actions (false);
1577 /* propagate into backend */
1580 if (!selection->regions.empty()) {
1581 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1583 _session->clear_object_selection ();
1587 if ( _session->solo_selection_active() )
1588 play_solo_selection(false);
1592 Editor::point_selection_changed ()
1594 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1595 (*i)->set_selected_points (selection->points);
1600 Editor::select_all_in_track (Selection::Operation op)
1602 list<Selectable *> touched;
1604 if (!clicked_routeview) {
1608 begin_reversible_selection_op (X_("Select All in Track"));
1610 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1613 case Selection::Toggle:
1614 selection->add (touched);
1616 case Selection::Set:
1617 selection->set (touched);
1619 case Selection::Extend:
1620 /* meaningless, because we're selecting everything */
1622 case Selection::Add:
1623 selection->add (touched);
1627 commit_reversible_selection_op ();
1631 Editor::select_all_internal_edit (Selection::Operation)
1633 bool selected = false;
1635 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1636 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1638 mrv->select_all_notes ();
1643 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1645 mrv->select_all_notes ();
1653 Editor::select_all_objects (Selection::Operation op)
1655 list<Selectable *> touched;
1657 if (internal_editing() && select_all_internal_edit(op)) {
1658 return; // Selected notes
1663 if (selection->tracks.empty()) {
1666 ts = selection->tracks;
1669 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1670 if ((*iter)->hidden()) {
1673 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1676 begin_reversible_selection_op (X_("select all"));
1678 case Selection::Add:
1679 selection->add (touched);
1681 case Selection::Toggle:
1682 selection->toggle (touched);
1684 case Selection::Set:
1685 selection->set (touched);
1687 case Selection::Extend:
1688 /* meaningless, because we're selecting everything */
1691 commit_reversible_selection_op ();
1695 Editor::invert_selection_in_track ()
1697 list<Selectable *> touched;
1699 if (!clicked_routeview) {
1703 begin_reversible_selection_op (X_("Invert Selection in Track"));
1704 clicked_routeview->get_inverted_selectables (*selection, touched);
1705 selection->set (touched);
1706 commit_reversible_selection_op ();
1710 Editor::invert_selection ()
1712 list<Selectable *> touched;
1714 if (internal_editing()) {
1715 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1716 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1718 mrv->invert_selection ();
1724 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1725 if ((*iter)->hidden()) {
1728 (*iter)->get_inverted_selectables (*selection, touched);
1731 begin_reversible_selection_op (X_("Invert Selection"));
1732 selection->set (touched);
1733 commit_reversible_selection_op ();
1736 /** @param start Start time in session samples.
1737 * @param end End time in session samples.
1738 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1739 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1740 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1741 * within the region are already selected.
1744 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1746 list<Selectable*> found;
1748 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1750 if ((*iter)->hidden()) {
1754 (*iter)->get_selectables (start, end, top, bot, found);
1757 if (found.empty()) {
1758 selection->clear_objects();
1759 selection->clear_time ();
1763 if (preserve_if_selected && op != Selection::Toggle) {
1764 list<Selectable*>::iterator i = found.begin();
1765 while (i != found.end() && (*i)->selected()) {
1769 if (i == found.end()) {
1774 begin_reversible_selection_op (X_("select all within"));
1776 case Selection::Add:
1777 selection->add (found);
1779 case Selection::Toggle:
1780 selection->toggle (found);
1782 case Selection::Set:
1783 selection->set (found);
1785 case Selection::Extend:
1786 /* not defined yet */
1790 commit_reversible_selection_op ();
1794 Editor::set_selection_from_region ()
1796 if (selection->regions.empty()) {
1800 /* find all the tracks that have selected regions */
1802 set<TimeAxisView*> tracks;
1804 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1805 tracks.insert (&(*r)->get_time_axis_view());
1809 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1811 /* select range (this will clear the region selection) */
1813 selection->set (selection->regions.start(), selection->regions.end_sample());
1815 /* and select the tracks */
1817 selection->set (tvl);
1819 if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1820 set_mouse_mode (Editing::MouseRange, false);
1825 Editor::set_selection_from_punch()
1829 if ((location = _session->locations()->auto_punch_location()) == 0) {
1833 set_selection_from_range (*location);
1837 Editor::set_selection_from_loop()
1841 if ((location = _session->locations()->auto_loop_location()) == 0) {
1844 set_selection_from_range (*location);
1848 Editor::set_selection_from_range (Location& loc)
1850 begin_reversible_selection_op (X_("set selection from range"));
1852 selection->set (loc.start(), loc.end());
1854 // if no tracks are selected, enable all tracks
1855 // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1856 if (selection->tracks.empty()) {
1857 select_all_tracks();
1860 commit_reversible_selection_op ();
1862 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1863 set_mouse_mode (MouseRange, false);
1868 Editor::select_all_selectables_using_time_selection ()
1870 list<Selectable *> touched;
1872 if (selection->time.empty()) {
1876 samplepos_t start = selection->time[clicked_selection].start;
1877 samplepos_t end = selection->time[clicked_selection].end;
1879 if (end - start < 1) {
1885 if (selection->tracks.empty()) {
1888 ts = &selection->tracks;
1891 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1892 if ((*iter)->hidden()) {
1895 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1898 begin_reversible_selection_op (X_("select all from range"));
1899 selection->set (touched);
1900 commit_reversible_selection_op ();
1905 Editor::select_all_selectables_using_punch()
1907 Location* location = _session->locations()->auto_punch_location();
1908 list<Selectable *> touched;
1910 if (location == 0 || (location->end() - location->start() <= 1)) {
1917 if (selection->tracks.empty()) {
1920 ts = &selection->tracks;
1923 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1924 if ((*iter)->hidden()) {
1927 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1929 begin_reversible_selection_op (X_("select all from punch"));
1930 selection->set (touched);
1931 commit_reversible_selection_op ();
1936 Editor::select_all_selectables_using_loop()
1938 Location* location = _session->locations()->auto_loop_location();
1939 list<Selectable *> touched;
1941 if (location == 0 || (location->end() - location->start() <= 1)) {
1948 if (selection->tracks.empty()) {
1951 ts = &selection->tracks;
1954 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1955 if ((*iter)->hidden()) {
1958 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1960 begin_reversible_selection_op (X_("select all from loop"));
1961 selection->set (touched);
1962 commit_reversible_selection_op ();
1967 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1971 list<Selectable *> touched;
1974 start = cursor->current_sample();
1975 end = _session->current_end_sample();
1977 if (cursor->current_sample() > 0) {
1979 end = cursor->current_sample() - 1;
1985 if (internal_editing()) {
1986 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1987 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1989 mrv->select_range (start, end);
1996 begin_reversible_selection_op (X_("select all after cursor"));
1998 begin_reversible_selection_op (X_("select all before cursor"));
2003 if (selection->tracks.empty()) {
2006 ts = &selection->tracks;
2009 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2010 if ((*iter)->hidden()) {
2013 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2015 selection->set (touched);
2016 commit_reversible_selection_op ();
2020 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2024 list<Selectable *> touched;
2027 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2028 end = _session->current_end_sample();
2030 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2038 if (internal_editing()) {
2039 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2040 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2041 mrv->select_range (start, end);
2047 begin_reversible_selection_op (X_("select all after edit"));
2049 begin_reversible_selection_op (X_("select all before edit"));
2054 if (selection->tracks.empty()) {
2057 ts = &selection->tracks;
2060 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2061 if ((*iter)->hidden()) {
2064 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2066 selection->set (touched);
2067 commit_reversible_selection_op ();
2071 Editor::select_all_selectables_between (bool within)
2075 list<Selectable *> touched;
2077 if (!get_edit_op_range (start, end)) {
2081 if (internal_editing()) {
2082 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2083 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2084 mrv->select_range (start, end);
2091 if (selection->tracks.empty()) {
2094 ts = &selection->tracks;
2097 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2098 if ((*iter)->hidden()) {
2101 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2104 begin_reversible_selection_op (X_("Select all Selectables Between"));
2105 selection->set (touched);
2106 commit_reversible_selection_op ();
2110 Editor::select_range_between ()
2115 if (!selection->time.empty()) {
2116 selection->clear_time ();
2119 if (!get_edit_op_range (start, end)) {
2123 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2124 set_mouse_mode (MouseRange, false);
2127 begin_reversible_selection_op (X_("Select Range Between"));
2128 selection->set (start, end);
2129 commit_reversible_selection_op ();
2133 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2138 /* if an explicit range exists, use it */
2140 if ((mouse_mode == MouseRange || get_smart_mode()) && !selection->time.empty()) {
2141 /* we know that these are ordered */
2142 start = selection->time.start();
2143 end = selection->time.end_sample();
2151 // if (!mouse_sample (m, ignored)) {
2152 // /* mouse is not in a canvas, try playhead+selected marker.
2153 // this is probably most true when using menus.
2156 // if (selection->markers.empty()) {
2160 // start = selection->markers.front()->position();
2161 // end = _session->audible_sample();
2165 // switch (_edit_point) {
2166 // case EditAtPlayhead:
2167 // if (selection->markers.empty()) {
2168 // /* use mouse + playhead */
2170 // end = _session->audible_sample();
2172 // /* use playhead + selected marker */
2173 // start = _session->audible_sample();
2174 // end = selection->markers.front()->position();
2178 // case EditAtMouse:
2179 // /* use mouse + selected marker */
2180 // if (selection->markers.empty()) {
2182 // end = _session->audible_sample();
2184 // start = selection->markers.front()->position();
2189 // case EditAtSelectedMarker:
2190 // /* use mouse + selected marker */
2191 // if (selection->markers.empty()) {
2193 // MessageDialog win (_("No edit range defined"),
2198 // win.set_secondary_text (
2199 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2202 // win.set_default_response (RESPONSE_CLOSE);
2203 // win.set_position (Gtk::WIN_POS_MOUSE);
2208 // return false; // NO RANGE
2210 // start = selection->markers.front()->position();
2216 // if (start == end) {
2220 // if (start > end) {
2221 // swap (start, end);
2224 /* turn range into one delimited by start...end,
2234 Editor::deselect_all ()
2236 begin_reversible_selection_op (X_("Deselect All"));
2237 selection->clear ();
2238 commit_reversible_selection_op ();
2242 Editor::select_range (samplepos_t s, samplepos_t e)
2244 begin_reversible_selection_op (X_("Select Range"));
2245 selection->add (clicked_axisview);
2246 selection->time.clear ();
2247 long ret = selection->set (s, e);
2248 commit_reversible_selection_op ();