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 (_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) {
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::time_selection_changed ()
1161 /* XXX this is superficially inefficient. Hide the selection in all
1162 * tracks, then show it in all selected tracks.
1164 * However, if you investigate what this actually does, it isn't
1165 * anywhere nearly as bad as it may appear. Remember: nothing is
1166 * redrawn or even recomputed during these two loops - that only
1167 * happens when we next render ...
1170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1171 (*i)->hide_selection ();
1174 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1175 (*i)->show_selection (selection->time);
1178 if (selection->time.empty()) {
1179 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1181 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1184 /* propagate into backend, but only when there is no drag or we are at
1185 * the end of a drag, otherwise this is too expensive (could case a
1186 * locate per mouse motion event.
1189 if (_session && !_drags->active()) {
1190 if (selection->time.length() != 0) {
1191 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1193 _session->clear_range_selection ();
1198 /** Set all region actions to have a given sensitivity */
1200 Editor::sensitize_all_region_actions (bool s)
1202 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1204 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1205 (*i)->set_sensitive (s);
1208 _all_region_actions_sensitized = s;
1211 /** Sensitize region-based actions.
1213 * This method is called from whenever we leave the canvas, either by moving
1214 * the pointer out of it, or by popping up a context menu. See
1215 * Editor::{entered,left}_track_canvas() for details there.
1218 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1220 bool have_selection = false;
1221 bool have_entered = false;
1222 bool have_edit_point = false;
1225 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1228 if (!selection->regions.empty()) {
1229 have_selection = true;
1230 rs = selection->regions;
1233 if (entered_regionview) {
1234 have_entered = true;
1235 rs.add (entered_regionview);
1238 if (rs.empty() && !selection->tracks.empty()) {
1240 /* no selected regions, but some selected tracks.
1243 if (_edit_point == EditAtMouse) {
1244 if (!within_track_canvas) {
1245 /* pointer is not in canvas, so edit point is meaningless */
1246 have_edit_point = false;
1248 /* inside canvas. we don't know where the edit
1249 point will be when an action is invoked, but
1250 assume it could intersect with a region.
1252 have_edit_point = true;
1255 RegionSelection at_edit_point;
1256 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1257 get_regions_at (at_edit_point, where, selection->tracks);
1258 if (!at_edit_point.empty()) {
1259 have_edit_point = true;
1262 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1267 //std::cerr << "\tfinal have selection: " << have_selection
1268 // << " have entered " << have_entered
1269 // << " have edit point " << have_edit_point
1270 // << " EP = " << enum_2_string (_edit_point)
1273 typedef std::map<std::string,RegionAction> RegionActionMap;
1275 _ignore_region_action = true;
1277 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1278 RegionActionTarget tgt = x->second.target;
1279 bool sensitive = false;
1281 if ((tgt & SelectedRegions) && have_selection) {
1283 } else if ((tgt & EnteredRegions) && have_entered) {
1285 } else if ((tgt & EditPointRegions) && have_edit_point) {
1289 x->second.action->set_sensitive (sensitive);
1292 /* Look through the regions that are selected and make notes about what we have got */
1294 bool have_audio = false;
1295 bool have_multichannel_audio = false;
1296 bool have_midi = false;
1297 bool have_locked = false;
1298 bool have_unlocked = false;
1299 bool have_video_locked = false;
1300 bool have_video_unlocked = false;
1301 bool have_position_lock_style_audio = false;
1302 bool have_position_lock_style_music = false;
1303 bool have_muted = false;
1304 bool have_unmuted = false;
1305 bool have_opaque = false;
1306 bool have_non_opaque = false;
1307 bool have_not_at_natural_position = false;
1308 bool have_envelope_active = false;
1309 bool have_envelope_inactive = false;
1310 bool have_non_unity_scale_amplitude = false;
1311 bool have_compound_regions = false;
1312 bool have_inactive_fade_in = false;
1313 bool have_inactive_fade_out = false;
1314 bool have_active_fade_in = false;
1315 bool have_active_fade_out = false;
1316 bool have_transients = false;
1318 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1320 boost::shared_ptr<Region> r = (*i)->region ();
1321 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1325 if (ar->n_channels() > 1) {
1326 have_multichannel_audio = true;
1330 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1334 if (r->is_compound()) {
1335 have_compound_regions = true;
1341 have_unlocked = true;
1344 if (r->video_locked()) {
1345 have_video_locked = true;
1347 have_video_unlocked = true;
1350 if (r->position_lock_style() == MusicTime) {
1351 have_position_lock_style_music = true;
1353 have_position_lock_style_audio = true;
1359 have_unmuted = true;
1365 have_non_opaque = true;
1368 if (!r->at_natural_position()) {
1369 have_not_at_natural_position = true;
1372 if (r->has_transients ()){
1373 have_transients = true;
1377 if (ar->envelope_active()) {
1378 have_envelope_active = true;
1380 have_envelope_inactive = true;
1383 if (ar->scale_amplitude() != 1) {
1384 have_non_unity_scale_amplitude = true;
1387 if (ar->fade_in_active ()) {
1388 have_active_fade_in = true;
1390 have_inactive_fade_in = true;
1393 if (ar->fade_out_active ()) {
1394 have_active_fade_out = true;
1396 have_inactive_fade_out = true;
1401 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1403 if (rs.size() > 1) {
1404 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1405 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1406 _region_actions->get_action("rename-region")->set_sensitive (false);
1408 /* XXX need to check whether there is than 1 per
1409 playlist, because otherwise this makes no sense.
1411 _region_actions->get_action("combine-regions")->set_sensitive (true);
1413 _region_actions->get_action("combine-regions")->set_sensitive (false);
1415 } else if (rs.size() == 1) {
1416 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1417 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1418 _region_actions->get_action("combine-regions")->set_sensitive (false);
1421 if (!have_multichannel_audio) {
1422 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1426 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1427 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1428 _region_actions->get_action("quantize-region")->set_sensitive (false);
1429 _region_actions->get_action("legatize-region")->set_sensitive (false);
1430 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1431 _region_actions->get_action("transform-region")->set_sensitive (false);
1432 _region_actions->get_action("fork-region")->set_sensitive (false);
1433 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1434 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1435 _region_actions->get_action("transpose-region")->set_sensitive (false);
1437 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1438 /* others were already marked sensitive */
1441 /* ok, moving along... */
1443 if (have_compound_regions) {
1444 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1446 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1451 if (have_envelope_active && !have_envelope_inactive) {
1452 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1453 } else if (have_envelope_active && have_envelope_inactive) {
1454 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1459 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1460 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1461 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1462 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1463 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1464 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1465 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1469 if (!have_non_unity_scale_amplitude || !have_audio) {
1470 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1473 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1474 a->set_active (have_locked && !have_unlocked);
1475 if (have_locked && have_unlocked) {
1476 // a->set_inconsistent ();
1479 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1480 a->set_active (have_video_locked && !have_video_unlocked);
1481 if (have_video_locked && have_video_unlocked) {
1482 // a->set_inconsistent ();
1485 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1486 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1488 vector<Widget*> proxies = a->get_proxies();
1489 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1490 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1492 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1496 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1497 a->set_active (have_muted && !have_unmuted);
1498 if (have_muted && have_unmuted) {
1499 // a->set_inconsistent ();
1502 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1503 a->set_active (have_opaque && !have_non_opaque);
1504 if (have_opaque && have_non_opaque) {
1505 // a->set_inconsistent ();
1508 if (!have_not_at_natural_position) {
1509 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1512 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1513 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1514 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1516 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1519 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1520 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1521 if (have_active_fade_in && have_inactive_fade_in) {
1522 // a->set_inconsistent ();
1525 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1526 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1528 if (have_active_fade_out && have_inactive_fade_out) {
1529 // a->set_inconsistent ();
1532 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1533 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1535 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1536 a->set_active (have_active_fade && !have_inactive_fade);
1538 if (have_active_fade && have_inactive_fade) {
1539 // a->set_inconsistent ();
1542 _ignore_region_action = false;
1544 _all_region_actions_sensitized = false;
1548 Editor::region_selection_changed ()
1550 _regions->block_change_connection (true);
1551 editor_regions_selection_changed_connection.block(true);
1553 if (_region_selection_change_updates_region_list) {
1554 _regions->unselect_all ();
1557 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1558 (*i)->set_selected_regionviews (selection->regions);
1561 if (_region_selection_change_updates_region_list) {
1562 _regions->set_selected (selection->regions);
1565 _regions->block_change_connection (false);
1566 editor_regions_selection_changed_connection.block(false);
1568 sensitize_the_right_region_actions (false);
1570 /* propagate into backend */
1573 if (!selection->regions.empty()) {
1574 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1576 _session->clear_object_selection ();
1583 Editor::point_selection_changed ()
1585 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1586 (*i)->set_selected_points (selection->points);
1591 Editor::select_all_in_track (Selection::Operation op)
1593 list<Selectable *> touched;
1595 if (!clicked_routeview) {
1599 begin_reversible_selection_op (X_("Select All in Track"));
1601 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1604 case Selection::Toggle:
1605 selection->add (touched);
1607 case Selection::Set:
1608 selection->set (touched);
1610 case Selection::Extend:
1611 /* meaningless, because we're selecting everything */
1613 case Selection::Add:
1614 selection->add (touched);
1618 commit_reversible_selection_op ();
1622 Editor::select_all_internal_edit (Selection::Operation)
1624 bool selected = false;
1626 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1627 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1629 mrv->select_all_notes ();
1634 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1636 mrv->select_all_notes ();
1644 Editor::select_all_objects (Selection::Operation op)
1646 list<Selectable *> touched;
1648 if (internal_editing() && select_all_internal_edit(op)) {
1649 return; // Selected notes
1654 if (selection->tracks.empty()) {
1657 ts = selection->tracks;
1660 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1661 if ((*iter)->hidden()) {
1664 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1667 begin_reversible_selection_op (X_("select all"));
1669 case Selection::Add:
1670 selection->add (touched);
1672 case Selection::Toggle:
1673 selection->toggle (touched);
1675 case Selection::Set:
1676 selection->set (touched);
1678 case Selection::Extend:
1679 /* meaningless, because we're selecting everything */
1682 commit_reversible_selection_op ();
1686 Editor::invert_selection_in_track ()
1688 list<Selectable *> touched;
1690 if (!clicked_routeview) {
1694 begin_reversible_selection_op (X_("Invert Selection in Track"));
1695 clicked_routeview->get_inverted_selectables (*selection, touched);
1696 selection->set (touched);
1697 commit_reversible_selection_op ();
1701 Editor::invert_selection ()
1703 list<Selectable *> touched;
1705 if (internal_editing()) {
1706 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1707 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1709 mrv->invert_selection ();
1715 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1716 if ((*iter)->hidden()) {
1719 (*iter)->get_inverted_selectables (*selection, touched);
1722 begin_reversible_selection_op (X_("Invert Selection"));
1723 selection->set (touched);
1724 commit_reversible_selection_op ();
1727 /** @param start Start time in session samples.
1728 * @param end End time in session samples.
1729 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1730 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1731 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1732 * within the region are already selected.
1735 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1737 list<Selectable*> found;
1739 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1741 if ((*iter)->hidden()) {
1745 (*iter)->get_selectables (start, end, top, bot, found);
1748 if (found.empty()) {
1749 selection->clear_objects();
1750 selection->clear_time ();
1754 if (preserve_if_selected && op != Selection::Toggle) {
1755 list<Selectable*>::iterator i = found.begin();
1756 while (i != found.end() && (*i)->selected()) {
1760 if (i == found.end()) {
1765 begin_reversible_selection_op (X_("select all within"));
1767 case Selection::Add:
1768 selection->add (found);
1770 case Selection::Toggle:
1771 selection->toggle (found);
1773 case Selection::Set:
1774 selection->set (found);
1776 case Selection::Extend:
1777 /* not defined yet */
1781 commit_reversible_selection_op ();
1785 Editor::set_selection_from_region ()
1787 if (selection->regions.empty()) {
1791 /* find all the tracks that have selected regions */
1793 set<TimeAxisView*> tracks;
1795 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1796 tracks.insert (&(*r)->get_time_axis_view());
1800 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1802 /* select range (this will clear the region selection) */
1804 selection->set (selection->regions.start(), selection->regions.end_sample());
1806 /* and select the tracks */
1808 selection->set (tvl);
1810 if (!get_smart_mode () || !mouse_mode == Editing::MouseObject) {
1811 set_mouse_mode (Editing::MouseRange, false);
1816 Editor::set_selection_from_punch()
1820 if ((location = _session->locations()->auto_punch_location()) == 0) {
1824 set_selection_from_range (*location);
1828 Editor::set_selection_from_loop()
1832 if ((location = _session->locations()->auto_loop_location()) == 0) {
1835 set_selection_from_range (*location);
1839 Editor::set_selection_from_range (Location& loc)
1841 begin_reversible_selection_op (X_("set selection from range"));
1842 selection->set (loc.start(), loc.end());
1843 commit_reversible_selection_op ();
1845 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1846 set_mouse_mode (MouseRange, false);
1851 Editor::select_all_selectables_using_time_selection ()
1853 list<Selectable *> touched;
1855 if (selection->time.empty()) {
1859 samplepos_t start = selection->time[clicked_selection].start;
1860 samplepos_t end = selection->time[clicked_selection].end;
1862 if (end - start < 1) {
1868 if (selection->tracks.empty()) {
1871 ts = &selection->tracks;
1874 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1875 if ((*iter)->hidden()) {
1878 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1881 begin_reversible_selection_op (X_("select all from range"));
1882 selection->set (touched);
1883 commit_reversible_selection_op ();
1888 Editor::select_all_selectables_using_punch()
1890 Location* location = _session->locations()->auto_punch_location();
1891 list<Selectable *> touched;
1893 if (location == 0 || (location->end() - location->start() <= 1)) {
1900 if (selection->tracks.empty()) {
1903 ts = &selection->tracks;
1906 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1907 if ((*iter)->hidden()) {
1910 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1912 begin_reversible_selection_op (X_("select all from punch"));
1913 selection->set (touched);
1914 commit_reversible_selection_op ();
1919 Editor::select_all_selectables_using_loop()
1921 Location* location = _session->locations()->auto_loop_location();
1922 list<Selectable *> touched;
1924 if (location == 0 || (location->end() - location->start() <= 1)) {
1931 if (selection->tracks.empty()) {
1934 ts = &selection->tracks;
1937 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1938 if ((*iter)->hidden()) {
1941 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1943 begin_reversible_selection_op (X_("select all from loop"));
1944 selection->set (touched);
1945 commit_reversible_selection_op ();
1950 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1954 list<Selectable *> touched;
1957 start = cursor->current_sample();
1958 end = _session->current_end_sample();
1960 if (cursor->current_sample() > 0) {
1962 end = cursor->current_sample() - 1;
1968 if (internal_editing()) {
1969 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1970 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1972 mrv->select_range (start, end);
1979 begin_reversible_selection_op (X_("select all after cursor"));
1981 begin_reversible_selection_op (X_("select all before cursor"));
1986 if (selection->tracks.empty()) {
1989 ts = &selection->tracks;
1992 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1993 if ((*iter)->hidden()) {
1996 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1998 selection->set (touched);
1999 commit_reversible_selection_op ();
2003 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2007 list<Selectable *> touched;
2010 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2011 end = _session->current_end_sample();
2013 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2021 if (internal_editing()) {
2022 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2023 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2024 mrv->select_range (start, end);
2030 begin_reversible_selection_op (X_("select all after edit"));
2032 begin_reversible_selection_op (X_("select all before edit"));
2037 if (selection->tracks.empty()) {
2040 ts = &selection->tracks;
2043 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2044 if ((*iter)->hidden()) {
2047 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2049 selection->set (touched);
2050 commit_reversible_selection_op ();
2054 Editor::select_all_selectables_between (bool within)
2058 list<Selectable *> touched;
2060 if (!get_edit_op_range (start, end)) {
2064 if (internal_editing()) {
2065 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2066 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2067 mrv->select_range (start, end);
2074 if (selection->tracks.empty()) {
2077 ts = &selection->tracks;
2080 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2081 if ((*iter)->hidden()) {
2084 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2087 begin_reversible_selection_op (X_("Select all Selectables Between"));
2088 selection->set (touched);
2089 commit_reversible_selection_op ();
2093 Editor::select_range_between ()
2098 if ( !selection->time.empty() ) {
2099 selection->clear_time ();
2102 if (!get_edit_op_range (start, end)) {
2106 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2107 set_mouse_mode (MouseRange, false);
2110 begin_reversible_selection_op (X_("Select Range Between"));
2111 selection->set (start, end);
2112 commit_reversible_selection_op ();
2116 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2121 /* if an explicit range exists, use it */
2123 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
2124 /* we know that these are ordered */
2125 start = selection->time.start();
2126 end = selection->time.end_sample();
2134 // if (!mouse_sample (m, ignored)) {
2135 // /* mouse is not in a canvas, try playhead+selected marker.
2136 // this is probably most true when using menus.
2139 // if (selection->markers.empty()) {
2143 // start = selection->markers.front()->position();
2144 // end = _session->audible_sample();
2148 // switch (_edit_point) {
2149 // case EditAtPlayhead:
2150 // if (selection->markers.empty()) {
2151 // /* use mouse + playhead */
2153 // end = _session->audible_sample();
2155 // /* use playhead + selected marker */
2156 // start = _session->audible_sample();
2157 // end = selection->markers.front()->position();
2161 // case EditAtMouse:
2162 // /* use mouse + selected marker */
2163 // if (selection->markers.empty()) {
2165 // end = _session->audible_sample();
2167 // start = selection->markers.front()->position();
2172 // case EditAtSelectedMarker:
2173 // /* use mouse + selected marker */
2174 // if (selection->markers.empty()) {
2176 // MessageDialog win (_("No edit range defined"),
2181 // win.set_secondary_text (
2182 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2185 // win.set_default_response (RESPONSE_CLOSE);
2186 // win.set_position (Gtk::WIN_POS_MOUSE);
2191 // return false; // NO RANGE
2193 // start = selection->markers.front()->position();
2199 // if (start == end) {
2203 // if (start > end) {
2204 // swap (start, end);
2207 /* turn range into one delimited by start...end,
2217 Editor::deselect_all ()
2219 begin_reversible_selection_op (X_("Deselect All"));
2220 selection->clear ();
2221 commit_reversible_selection_op ();
2225 Editor::select_range (samplepos_t s, samplepos_t e)
2227 begin_reversible_selection_op (X_("Select Range"));
2228 selection->add (clicked_axisview);
2229 selection->time.clear ();
2230 long ret = selection->set (s, e);
2231 commit_reversible_selection_op ();