2 Copyright (C) 2000-2006 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 #include "pbd/stacktrace.h"
24 #include "pbd/unwind.h"
26 #include "ardour/control_protocol_manager.h"
27 #include "ardour/midi_region.h"
28 #include "ardour/playlist.h"
29 #include "ardour/profile.h"
30 #include "ardour/route_group.h"
31 #include "ardour/selection.h"
32 #include "ardour/session.h"
33 #include "ardour/vca.h"
36 #include "editor_drag.h"
37 #include "editor_routes.h"
39 #include "audio_time_axis.h"
40 #include "audio_region_view.h"
41 #include "audio_streamview.h"
42 #include "automation_line.h"
43 #include "control_point.h"
44 #include "editor_regions.h"
45 #include "editor_cursors.h"
46 #include "midi_region_view.h"
52 using namespace ARDOUR;
56 using namespace Gtkmm2ext;
57 using namespace Editing;
59 struct TrackViewByPositionSorter
61 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
62 return a->y_position() < b->y_position();
67 Editor::extend_selection_to_track (TimeAxisView& view)
69 if (selection->selected (&view)) {
70 /* already selected, do nothing */
74 if (selection->tracks.empty()) {
76 if (!selection->selected (&view)) {
77 selection->set (&view);
84 /* something is already selected, so figure out which range of things to add */
86 TrackViewList to_be_added;
87 TrackViewList sorted = track_views;
88 TrackViewByPositionSorter cmp;
89 bool passed_clicked = false;
94 /* figure out if we should go forward or backwards */
96 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
99 passed_clicked = true;
102 if (selection->selected (*i)) {
103 if (passed_clicked) {
112 passed_clicked = false;
116 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
119 passed_clicked = true;
123 if (passed_clicked) {
124 if ((*i)->hidden()) {
127 if (selection->selected (*i)) {
129 } else if (!(*i)->hidden()) {
130 to_be_added.push_back (*i);
137 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
140 passed_clicked = true;
144 if (passed_clicked) {
146 if ((*r)->hidden()) {
150 if (selection->selected (*r)) {
152 } else if (!(*r)->hidden()) {
153 to_be_added.push_back (*r);
159 if (!selection->selected (&view)) {
160 to_be_added.push_back (&view);
163 if (!to_be_added.empty()) {
164 selection->add (to_be_added);
172 Editor::select_all_tracks ()
174 TrackViewList visible_views;
175 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
176 if ((*i)->marked_for_display()) {
177 visible_views.push_back (*i);
180 PBD::Unwinder<bool> uw (_track_selection_change_without_scroll, true);
181 selection->set (visible_views);
184 /** Select clicked_axisview, unless there are no currently selected
185 * tracks, in which case nothing will happen unless `force' is true.
188 Editor::set_selected_track_as_side_effect (Selection::Operation op)
190 if (!clicked_axisview) {
194 PBD::Unwinder<bool> uw (_editor_track_selection_change_without_scroll, true);
196 RouteGroup* group = NULL;
197 if (clicked_routeview) {
198 group = clicked_routeview->route()->route_group();
202 case Selection::Toggle:
203 if (selection->selected (clicked_axisview)) {
204 if (group && group->is_active()) {
205 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
206 if ((*i)->route_group() == group) {
207 selection->remove(*i);
211 selection->remove (clicked_axisview);
214 if (group && group->is_active()) {
215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216 if ((*i)->route_group() == group) {
221 selection->add (clicked_axisview);
227 if (group && group->is_active()) {
228 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
229 if ((*i)->route_group() == group) {
234 selection->add (clicked_axisview);
240 if (group && group->is_active()) {
241 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
242 if ((*i)->route_group() == group) {
247 selection->set (clicked_axisview);
251 case Selection::Extend:
258 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
260 begin_reversible_selection_op (X_("Set Selected Track"));
263 case Selection::Toggle:
264 if (selection->selected (&view)) {
266 selection->remove (&view);
269 selection->add (&view);
274 selection->add (&view);
278 selection->set (&view);
281 case Selection::Extend:
282 extend_selection_to_track (view);
286 commit_reversible_selection_op ();
290 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
292 if (!clicked_routeview) {
300 set_selected_track (*clicked_routeview, op, no_remove);
304 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
306 if (!clicked_control_point) {
314 if (!selection->selected (clicked_control_point)) {
315 selection->set (clicked_control_point);
318 /* clicked on an already selected point */
322 if (selection->points.size() > 1) {
323 selection->set (clicked_control_point);
332 selection->add (clicked_control_point);
336 case Selection::Toggle:
338 /* This is a bit of a hack; if we Primary-Click-Drag a control
339 point (for push drag) we want the point we clicked on to be
340 selected, otherwise we end up confusingly dragging an
341 unselected point. So here we ensure that the point is selected
342 after the press, and if we subsequently get a release (meaning no
343 drag occurred) we set things up so that the toggle has happened.
345 if (press && !selection->selected (clicked_control_point)) {
346 /* This is the button press, and the control point is not selected; make it so,
347 in case this press leads to a drag. Also note that having done this, we don't
348 need to toggle again on release.
350 selection->toggle (clicked_control_point);
351 _control_point_toggled_on_press = true;
353 } else if (!press && !_control_point_toggled_on_press) {
354 /* This is the release, and the point wasn't toggled on the press, so do it now */
355 selection->toggle (clicked_control_point);
359 _control_point_toggled_on_press = false;
362 case Selection::Extend:
371 Editor::get_onscreen_tracks (TrackViewList& tvl)
373 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
374 if ((*i)->y_position() < _visible_canvas_height) {
380 /** Call a slot for a given `basis' track and also for any track that is in the same
381 * active route group with a particular set of properties.
383 * @param sl Slot to call.
384 * @param basis Basis track.
385 * @param prop Properties that active edit groups must share to be included in the map.
389 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
391 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
393 if (route_basis == 0) {
397 set<RouteTimeAxisView*> tracks;
398 tracks.insert (route_basis);
400 RouteGroup* group = route_basis->route()->route_group();
402 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
404 /* the basis is a member of an active route group, with the appropriate
405 properties; find other members */
407 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
408 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
409 if (v && v->route()->route_group() == group) {
416 uint32_t const sz = tracks.size ();
418 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
423 /** Call a slot for a given `basis' track and also for any track that is in the same
424 * active route group with a particular set of properties.
426 * @param sl Slot to call.
427 * @param basis Basis track.
428 * @param prop Properties that active edit groups must share to be included in the map.
432 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
434 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
435 set<boost::shared_ptr<Playlist> > playlists;
437 if (route_basis == 0) {
441 set<RouteTimeAxisView*> tracks;
442 tracks.insert (route_basis);
444 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
446 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id)) {
448 /* the basis is a member of an active route group, with the appropriate
449 properties; find other members */
451 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
452 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
454 if (v && v->route()->route_group() == group) {
456 boost::shared_ptr<Track> t = v->track();
458 if (playlists.insert (t->playlist()).second) {
459 /* haven't seen this playlist yet */
463 /* not actually a "Track", but a timeaxis view that
464 we should mapover anyway.
473 uint32_t const sz = tracks.size ();
475 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
481 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
483 boost::shared_ptr<Playlist> pl;
484 vector<boost::shared_ptr<Region> > results;
486 boost::shared_ptr<Track> tr;
488 if ((tr = tv.track()) == 0) {
493 if (&tv == &basis->get_time_axis_view()) {
494 /* looking in same track as the original */
498 if ((pl = tr->playlist()) != 0) {
499 pl->get_equivalent_regions (basis->region(), results);
502 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
503 if ((marv = tv.view()->find_view (*ir)) != 0) {
504 all_equivs->push_back (marv);
510 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
512 mapover_tracks_with_unique_playlists (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
514 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
516 equivalent_regions.push_back (basis);
520 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
522 RegionSelection equivalent;
524 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
526 vector<RegionView*> eq;
528 mapover_tracks_with_unique_playlists (
529 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
530 &(*i)->get_time_axis_view(), prop);
532 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
543 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
545 int region_count = 0;
547 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
549 RouteTimeAxisView* tatv;
551 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
553 boost::shared_ptr<Playlist> pl;
554 vector<boost::shared_ptr<Region> > results;
556 boost::shared_ptr<Track> tr;
558 if ((tr = tatv->track()) == 0) {
563 if ((pl = (tr->playlist())) != 0) {
564 pl->get_region_list_equivalent_regions (region, results);
567 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
568 if ((marv = tatv->view()->find_view (*ir)) != 0) {
581 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
583 vector<RegionView*> all_equivalent_regions;
586 if (!clicked_regionview || !clicked_routeview) {
591 button_release_can_deselect = false;
594 if (op == Selection::Toggle || op == Selection::Set) {
597 case Selection::Toggle:
598 if (selection->selected (clicked_regionview)) {
601 /* whatever was clicked was selected already; do nothing here but allow
602 the button release to deselect it
605 button_release_can_deselect = true;
608 if (button_release_can_deselect) {
610 /* just remove this one region, but only on a permitted button release */
612 selection->remove (clicked_regionview);
615 /* no more deselect action on button release till a new press
616 finds an already selected object.
619 button_release_can_deselect = false;
627 if (selection->selected (clicked_routeview)) {
628 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
630 all_equivalent_regions.push_back (clicked_regionview);
633 /* add all the equivalent regions, but only on button press */
635 if (!all_equivalent_regions.empty()) {
639 selection->add (all_equivalent_regions);
645 if (!selection->selected (clicked_regionview)) {
646 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
647 selection->set (all_equivalent_regions);
650 /* clicked on an already selected region */
654 if (selection->regions.size() > 1) {
655 /* collapse region selection down to just this one region (and its equivalents) */
656 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
657 selection->set(all_equivalent_regions);
669 } else if (op == Selection::Extend) {
671 list<Selectable*> results;
672 samplepos_t last_sample;
673 samplepos_t first_sample;
674 bool same_track = false;
676 /* 1. find the last selected regionview in the track that was clicked in */
679 first_sample = max_samplepos;
681 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
682 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
684 if ((*x)->region()->last_sample() > last_sample) {
685 last_sample = (*x)->region()->last_sample();
688 if ((*x)->region()->first_sample() < first_sample) {
689 first_sample = (*x)->region()->first_sample();
698 /* 2. figure out the boundaries for our search for new objects */
700 switch (clicked_regionview->region()->coverage (first_sample, last_sample)) {
701 case Evoral::OverlapNone:
702 if (last_sample < clicked_regionview->region()->first_sample()) {
703 first_sample = last_sample;
704 last_sample = clicked_regionview->region()->last_sample();
706 last_sample = first_sample;
707 first_sample = clicked_regionview->region()->first_sample();
711 case Evoral::OverlapExternal:
712 if (last_sample < clicked_regionview->region()->first_sample()) {
713 first_sample = last_sample;
714 last_sample = clicked_regionview->region()->last_sample();
716 last_sample = first_sample;
717 first_sample = clicked_regionview->region()->first_sample();
721 case Evoral::OverlapInternal:
722 if (last_sample < clicked_regionview->region()->first_sample()) {
723 first_sample = last_sample;
724 last_sample = clicked_regionview->region()->last_sample();
726 last_sample = first_sample;
727 first_sample = clicked_regionview->region()->first_sample();
731 case Evoral::OverlapStart:
732 case Evoral::OverlapEnd:
733 /* nothing to do except add clicked region to selection, since it
734 overlaps with the existing selection in this track.
741 /* click in a track that has no regions selected, so extend vertically
742 to pick out all regions that are defined by the existing selection
747 first_sample = clicked_regionview->region()->position();
748 last_sample = clicked_regionview->region()->last_sample();
750 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
751 if ((*i)->region()->position() < first_sample) {
752 first_sample = (*i)->region()->position();
754 if ((*i)->region()->last_sample() + 1 > last_sample) {
755 last_sample = (*i)->region()->last_sample();
760 /* 2. find all the tracks we should select in */
762 set<RouteTimeAxisView*> relevant_tracks;
764 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
765 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
767 relevant_tracks.insert (r);
771 set<RouteTimeAxisView*> already_in_selection;
773 if (relevant_tracks.empty()) {
775 /* no tracks selected .. thus .. if the
776 regionview we're in isn't selected
777 (i.e. we're about to extend to it), then
778 find all tracks between the this one and
782 if (!selection->selected (clicked_regionview)) {
784 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
788 /* add this track to the ones we will search */
790 relevant_tracks.insert (rtv);
792 /* find the track closest to this one that
793 already a selected region.
796 RouteTimeAxisView* closest = 0;
797 int distance = INT_MAX;
798 int key = rtv->route()->presentation_info().order ();
800 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
802 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
804 if (artv && artv != rtv) {
806 pair<set<RouteTimeAxisView*>::iterator,bool> result;
808 result = already_in_selection.insert (artv);
811 /* newly added to already_in_selection */
813 int d = artv->route()->presentation_info().order ();
817 if (abs (d) < distance) {
827 /* now add all tracks between that one and this one */
829 int okey = closest->route()->presentation_info().order ();
835 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
836 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
837 if (artv && artv != rtv) {
839 int k = artv->route()->presentation_info().order ();
841 if (k >= okey && k <= key) {
843 /* in range but don't add it if
844 it already has tracks selected.
845 this avoids odd selection
846 behaviour that feels wrong.
849 if (find (already_in_selection.begin(),
850 already_in_selection.end(),
851 artv) == already_in_selection.end()) {
853 relevant_tracks.insert (artv);
863 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
864 one that was clicked.
867 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
868 (*t)->get_selectables (first_sample, last_sample, -1.0, -1.0, results);
871 /* 4. convert to a vector of regions */
873 vector<RegionView*> regions;
875 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
878 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
879 regions.push_back (arv);
883 if (!regions.empty()) {
884 selection->add (regions);
886 } else if (selection->regions.empty() && !selection->selected (clicked_regionview)) {
887 /* ensure that at least the clicked regionview is selected. */
888 selection->set (clicked_regionview);
899 Editor::set_selection (std::list<Selectable*> s, Selection::Operation op)
904 begin_reversible_selection_op (X_("set selection"));
906 case Selection::Toggle:
907 selection->toggle (s);
912 case Selection::Extend:
920 commit_reversible_selection_op () ;
924 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
926 vector<RegionView*> all_equivalent_regions;
928 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
930 if (all_equivalent_regions.empty()) {
934 begin_reversible_selection_op (X_("set selected regions"));
937 case Selection::Toggle:
938 /* XXX this is not correct */
939 selection->toggle (all_equivalent_regions);
942 selection->set (all_equivalent_regions);
944 case Selection::Extend:
945 selection->add (all_equivalent_regions);
948 selection->add (all_equivalent_regions);
952 commit_reversible_selection_op () ;
956 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
959 boost::shared_ptr<Region> r (weak_r.lock());
965 if ((rv = sv->find_view (r)) == 0) {
969 /* don't reset the selection if its something other than
970 a single other region.
973 if (selection->regions.size() > 1) {
977 begin_reversible_selection_op (X_("set selected regions"));
981 commit_reversible_selection_op () ;
986 struct SelectionOrderSorter {
987 bool operator() (TimeAxisView const * const a, TimeAxisView const * const b) const {
988 boost::shared_ptr<Stripable> sa = a->stripable ();
989 boost::shared_ptr<Stripable> sb = b->stripable ();
999 return sa->presentation_info().selection_cnt() < sb->presentation_info().selection_cnt();
1004 Editor::presentation_info_changed (PropertyChange const & what_changed)
1006 uint32_t n_tracks = 0;
1007 uint32_t n_busses = 0;
1008 uint32_t n_vcas = 0;
1009 uint32_t n_routes = 0;
1010 uint32_t n_stripables = 0;
1012 /* We cannot ensure ordering of the handlers for
1013 * PresentationInfo::Changed, so we have to do everything in order
1014 * here, as a single handler.
1017 if (what_changed.contains (Properties::selected)) {
1018 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1019 (*i)->set_selected (false);
1020 (*i)->hide_selection ();
1024 /* STEP 1: set the GUI selection state (in which TimeAxisViews for the
1025 * currently selected stripable/controllable duples are found and added
1028 selection->core_selection_changed (what_changed);
1030 /* STEP 2: update TimeAxisView's knowledge of their selected state
1033 if (what_changed.contains (Properties::selected)) {
1035 StripableNotificationListPtr stripables (new StripableNotificationList);
1037 switch (selection->tracks.size()) {
1041 set_selected_mixer_strip (*(selection->tracks.back()));
1042 if (!_track_selection_change_without_scroll && !_editor_track_selection_change_without_scroll) {
1043 ensure_time_axis_view_is_visible (*(selection->tracks.back()), false);
1048 CoreSelection::StripableAutomationControls sc;
1049 _session->selection().get_stripables (sc);
1051 for (CoreSelection::StripableAutomationControls::const_iterator i = sc.begin(); i != sc.end(); ++i) {
1053 AxisView* av = axis_view_by_stripable ((*i).stripable);
1061 if (boost::dynamic_pointer_cast<Track> ((*i).stripable)) {
1064 } else if (boost::dynamic_pointer_cast<Route> ((*i).stripable)) {
1067 } else if (boost::dynamic_pointer_cast<VCA> ((*i).stripable)) {
1071 TimeAxisView* tav = dynamic_cast<TimeAxisView*> (av);
1075 continue; /* impossible */
1078 if (!(*i).controllable) {
1080 /* "parent" track selected */
1081 tav->set_selected (true);
1082 tav->reshow_selection (selection->time);
1086 /* possibly a child */
1088 TimeAxisView::Children c = tav->get_child_list ();
1090 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
1092 boost::shared_ptr<AutomationControl> control = (*j)->control ();
1094 if (control != (*i).controllable) {
1098 (*j)->set_selected (true);
1099 (*j)->reshow_selection (selection->time);
1103 stripables->push_back ((*i).stripable);
1106 ActionManager::set_sensitive (ActionManager::stripable_selection_sensitive_actions, (n_stripables > 0));
1107 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, (n_tracks > 0));
1108 ActionManager::set_sensitive (ActionManager::bus_selection_sensitive_actions, (n_busses > 0));
1109 ActionManager::set_sensitive (ActionManager::route_selection_sensitive_actions, (n_routes > 0));
1110 ActionManager::set_sensitive (ActionManager::vca_selection_sensitive_actions, (n_vcas > 0));
1112 sensitize_the_right_region_actions (false);
1114 /* STEP 4: notify control protocols */
1116 ControlProtocolManager::instance().stripable_selection_changed (stripables);
1118 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1119 uint32_t audio_track_cnt = 0;
1120 uint32_t midi_track_cnt = 0;
1122 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1123 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1126 if (atv->is_audio_track()) {
1131 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1134 if (mtv->is_midi_track()) {
1141 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1145 /* STEP 4: update EditorRoutes treeview */
1149 soh.add (Properties::selected);
1150 soh.add (Properties::order);
1151 soh.add (Properties::hidden);
1153 if (what_changed.contains (soh)) {
1154 _routes->sync_treeview_from_presentation_info (what_changed);
1159 Editor::track_selection_changed ()
1161 /* reset paste count, so the plaste location doesn't get incremented
1162 * if we want to paste in the same place, but different track. */
1165 if ( _session->solo_selection_active() )
1166 play_solo_selection(false);
1170 Editor::time_selection_changed ()
1172 /* XXX this is superficially inefficient. Hide the selection in all
1173 * tracks, then show it in all selected tracks.
1175 * However, if you investigate what this actually does, it isn't
1176 * anywhere nearly as bad as it may appear. Remember: nothing is
1177 * redrawn or even recomputed during these two loops - that only
1178 * happens when we next render ...
1181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1182 (*i)->hide_selection ();
1185 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1186 (*i)->show_selection (selection->time);
1189 if (selection->time.empty()) {
1190 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1192 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1195 /* propagate into backend, but only when there is no drag or we are at
1196 * the end of a drag, otherwise this is too expensive (could case a
1197 * locate per mouse motion event.
1200 if (_session && !_drags->active()) {
1201 if (selection->time.length() != 0) {
1202 _session->set_range_selection (selection->time.start(), selection->time.end_sample());
1204 _session->clear_range_selection ();
1209 /** Set all region actions to have a given sensitivity */
1211 Editor::sensitize_all_region_actions (bool s)
1213 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1215 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1216 (*i)->set_sensitive (s);
1219 _all_region_actions_sensitized = s;
1222 /** Sensitize region-based actions.
1224 * This method is called from whenever we leave the canvas, either by moving
1225 * the pointer out of it, or by popping up a context menu. See
1226 * Editor::{entered,left}_track_canvas() for details there.
1229 Editor::sensitize_the_right_region_actions (bool because_canvas_crossing)
1231 bool have_selection = false;
1232 bool have_entered = false;
1233 bool have_edit_point = false;
1236 // std::cerr << "STRRA: crossing ? " << because_canvas_crossing << " within ? " << within_track_canvas
1239 if (!selection->regions.empty()) {
1240 have_selection = true;
1241 rs = selection->regions;
1244 if (entered_regionview) {
1245 have_entered = true;
1246 rs.add (entered_regionview);
1249 if (rs.empty() && !selection->tracks.empty()) {
1251 /* no selected regions, but some selected tracks.
1254 if (_edit_point == EditAtMouse) {
1255 if (!within_track_canvas) {
1256 /* pointer is not in canvas, so edit point is meaningless */
1257 have_edit_point = false;
1259 /* inside canvas. we don't know where the edit
1260 point will be when an action is invoked, but
1261 assume it could intersect with a region.
1263 have_edit_point = true;
1266 RegionSelection at_edit_point;
1267 samplepos_t const where = get_preferred_edit_position (Editing::EDIT_IGNORE_NONE, false, !within_track_canvas);
1268 get_regions_at (at_edit_point, where, selection->tracks);
1269 if (!at_edit_point.empty()) {
1270 have_edit_point = true;
1273 rs.insert (rs.end(), at_edit_point.begin(), at_edit_point.end());
1278 //std::cerr << "\tfinal have selection: " << have_selection
1279 // << " have entered " << have_entered
1280 // << " have edit point " << have_edit_point
1281 // << " EP = " << enum_2_string (_edit_point)
1284 typedef std::map<std::string,RegionAction> RegionActionMap;
1286 _ignore_region_action = true;
1288 for (RegionActionMap::iterator x = region_action_map.begin(); x != region_action_map.end(); ++x) {
1289 RegionActionTarget tgt = x->second.target;
1290 bool sensitive = false;
1292 if ((tgt & SelectedRegions) && have_selection) {
1294 } else if ((tgt & EnteredRegions) && have_entered) {
1296 } else if ((tgt & EditPointRegions) && have_edit_point) {
1300 x->second.action->set_sensitive (sensitive);
1303 /* Look through the regions that are selected and make notes about what we have got */
1305 bool have_audio = false;
1306 bool have_multichannel_audio = false;
1307 bool have_midi = false;
1308 bool have_locked = false;
1309 bool have_unlocked = false;
1310 bool have_video_locked = false;
1311 bool have_video_unlocked = false;
1312 bool have_position_lock_style_audio = false;
1313 bool have_position_lock_style_music = false;
1314 bool have_muted = false;
1315 bool have_unmuted = false;
1316 bool have_opaque = false;
1317 bool have_non_opaque = false;
1318 bool have_not_at_natural_position = false;
1319 bool have_envelope_active = false;
1320 bool have_envelope_inactive = false;
1321 bool have_non_unity_scale_amplitude = false;
1322 bool have_compound_regions = false;
1323 bool have_inactive_fade_in = false;
1324 bool have_inactive_fade_out = false;
1325 bool have_active_fade_in = false;
1326 bool have_active_fade_out = false;
1327 bool have_transients = false;
1329 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1331 boost::shared_ptr<Region> r = (*i)->region ();
1332 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1336 if (ar->n_channels() > 1) {
1337 have_multichannel_audio = true;
1341 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1345 if (r->is_compound()) {
1346 have_compound_regions = true;
1352 have_unlocked = true;
1355 if (r->video_locked()) {
1356 have_video_locked = true;
1358 have_video_unlocked = true;
1361 if (r->position_lock_style() == MusicTime) {
1362 have_position_lock_style_music = true;
1364 have_position_lock_style_audio = true;
1370 have_unmuted = true;
1376 have_non_opaque = true;
1379 if (!r->at_natural_position()) {
1380 have_not_at_natural_position = true;
1383 if (r->has_transients ()){
1384 have_transients = true;
1388 if (ar->envelope_active()) {
1389 have_envelope_active = true;
1391 have_envelope_inactive = true;
1394 if (ar->scale_amplitude() != 1) {
1395 have_non_unity_scale_amplitude = true;
1398 if (ar->fade_in_active ()) {
1399 have_active_fade_in = true;
1401 have_inactive_fade_in = true;
1404 if (ar->fade_out_active ()) {
1405 have_active_fade_out = true;
1407 have_inactive_fade_out = true;
1412 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1414 if (rs.size() > 1) {
1415 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1416 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1417 _region_actions->get_action("rename-region")->set_sensitive (false);
1419 /* XXX need to check whether there is than 1 per
1420 playlist, because otherwise this makes no sense.
1422 _region_actions->get_action("combine-regions")->set_sensitive (true);
1424 _region_actions->get_action("combine-regions")->set_sensitive (false);
1426 } else if (rs.size() == 1) {
1427 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1428 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1429 _region_actions->get_action("combine-regions")->set_sensitive (false);
1432 if (!have_multichannel_audio) {
1433 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1437 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1438 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1439 _region_actions->get_action("quantize-region")->set_sensitive (false);
1440 _region_actions->get_action("legatize-region")->set_sensitive (false);
1441 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1442 _region_actions->get_action("transform-region")->set_sensitive (false);
1443 _region_actions->get_action("fork-region")->set_sensitive (false);
1444 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1445 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1446 _region_actions->get_action("transpose-region")->set_sensitive (false);
1448 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1449 /* others were already marked sensitive */
1452 /* ok, moving along... */
1454 if (have_compound_regions) {
1455 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1457 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1462 if (have_envelope_active && !have_envelope_inactive) {
1463 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1464 } else if (have_envelope_active && have_envelope_inactive) {
1465 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1470 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1471 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1472 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1473 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1474 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1475 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1476 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1480 if (!have_non_unity_scale_amplitude || !have_audio) {
1481 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1484 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1485 a->set_active (have_locked && !have_unlocked);
1486 if (have_locked && have_unlocked) {
1487 // a->set_inconsistent ();
1490 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1491 a->set_active (have_video_locked && !have_video_unlocked);
1492 if (have_video_locked && have_video_unlocked) {
1493 // a->set_inconsistent ();
1496 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1497 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1499 vector<Widget*> proxies = a->get_proxies();
1500 for (vector<Widget*>::iterator p = proxies.begin(); p != proxies.end(); ++p) {
1501 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (*p);
1503 cmi->set_inconsistent (have_position_lock_style_music && have_position_lock_style_audio);
1507 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1508 a->set_active (have_muted && !have_unmuted);
1509 if (have_muted && have_unmuted) {
1510 // a->set_inconsistent ();
1513 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1514 a->set_active (have_opaque && !have_non_opaque);
1515 if (have_opaque && have_non_opaque) {
1516 // a->set_inconsistent ();
1519 if (!have_not_at_natural_position) {
1520 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1523 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1524 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1525 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1527 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1530 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1531 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1532 if (have_active_fade_in && have_inactive_fade_in) {
1533 // a->set_inconsistent ();
1536 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1537 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1539 if (have_active_fade_out && have_inactive_fade_out) {
1540 // a->set_inconsistent ();
1543 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1544 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1546 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1547 a->set_active (have_active_fade && !have_inactive_fade);
1549 if (have_active_fade && have_inactive_fade) {
1550 // a->set_inconsistent ();
1553 _ignore_region_action = false;
1555 _all_region_actions_sensitized = false;
1559 Editor::region_selection_changed ()
1561 _regions->block_change_connection (true);
1562 editor_regions_selection_changed_connection.block(true);
1564 if (_region_selection_change_updates_region_list) {
1565 _regions->unselect_all ();
1568 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1569 (*i)->set_selected_regionviews (selection->regions);
1572 if (_region_selection_change_updates_region_list) {
1573 _regions->set_selected (selection->regions);
1576 _regions->block_change_connection (false);
1577 editor_regions_selection_changed_connection.block(false);
1579 sensitize_the_right_region_actions (false);
1581 /* propagate into backend */
1584 if (!selection->regions.empty()) {
1585 _session->set_object_selection (selection->regions.start(), selection->regions.end_sample());
1587 _session->clear_object_selection ();
1591 if ( _session->solo_selection_active() )
1592 play_solo_selection(false);
1596 Editor::point_selection_changed ()
1598 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1599 (*i)->set_selected_points (selection->points);
1604 Editor::select_all_in_track (Selection::Operation op)
1606 list<Selectable *> touched;
1608 if (!clicked_routeview) {
1612 begin_reversible_selection_op (X_("Select All in Track"));
1614 clicked_routeview->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1617 case Selection::Toggle:
1618 selection->add (touched);
1620 case Selection::Set:
1621 selection->set (touched);
1623 case Selection::Extend:
1624 /* meaningless, because we're selecting everything */
1626 case Selection::Add:
1627 selection->add (touched);
1631 commit_reversible_selection_op ();
1635 Editor::select_all_internal_edit (Selection::Operation)
1637 bool selected = false;
1639 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1640 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1642 mrv->select_all_notes ();
1647 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1649 mrv->select_all_notes ();
1657 Editor::select_all_objects (Selection::Operation op)
1659 list<Selectable *> touched;
1661 if (internal_editing() && select_all_internal_edit(op)) {
1662 return; // Selected notes
1667 if (selection->tracks.empty()) {
1670 ts = selection->tracks;
1673 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1674 if ((*iter)->hidden()) {
1677 (*iter)->get_selectables (0, max_samplepos, 0, DBL_MAX, touched);
1680 begin_reversible_selection_op (X_("select all"));
1682 case Selection::Add:
1683 selection->add (touched);
1685 case Selection::Toggle:
1686 selection->toggle (touched);
1688 case Selection::Set:
1689 selection->set (touched);
1691 case Selection::Extend:
1692 /* meaningless, because we're selecting everything */
1695 commit_reversible_selection_op ();
1699 Editor::invert_selection_in_track ()
1701 list<Selectable *> touched;
1703 if (!clicked_routeview) {
1707 begin_reversible_selection_op (X_("Invert Selection in Track"));
1708 clicked_routeview->get_inverted_selectables (*selection, touched);
1709 selection->set (touched);
1710 commit_reversible_selection_op ();
1714 Editor::invert_selection ()
1716 list<Selectable *> touched;
1718 if (internal_editing()) {
1719 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1720 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1722 mrv->invert_selection ();
1728 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1729 if ((*iter)->hidden()) {
1732 (*iter)->get_inverted_selectables (*selection, touched);
1735 begin_reversible_selection_op (X_("Invert Selection"));
1736 selection->set (touched);
1737 commit_reversible_selection_op ();
1740 /** @param start Start time in session samples.
1741 * @param end End time in session samples.
1742 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1743 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1744 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1745 * within the region are already selected.
1748 Editor::select_all_within (samplepos_t start, samplepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1750 list<Selectable*> found;
1752 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1754 if ((*iter)->hidden()) {
1758 (*iter)->get_selectables (start, end, top, bot, found);
1761 if (found.empty()) {
1762 selection->clear_objects();
1763 selection->clear_time ();
1767 if (preserve_if_selected && op != Selection::Toggle) {
1768 list<Selectable*>::iterator i = found.begin();
1769 while (i != found.end() && (*i)->selected()) {
1773 if (i == found.end()) {
1778 begin_reversible_selection_op (X_("select all within"));
1780 case Selection::Add:
1781 selection->add (found);
1783 case Selection::Toggle:
1784 selection->toggle (found);
1786 case Selection::Set:
1787 selection->set (found);
1789 case Selection::Extend:
1790 /* not defined yet */
1794 commit_reversible_selection_op ();
1798 Editor::set_selection_from_region ()
1800 if (selection->regions.empty()) {
1804 /* find all the tracks that have selected regions */
1806 set<TimeAxisView*> tracks;
1808 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1809 tracks.insert (&(*r)->get_time_axis_view());
1813 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1815 /* select range (this will clear the region selection) */
1817 selection->set (selection->regions.start(), selection->regions.end_sample());
1819 /* and select the tracks */
1821 selection->set (tvl);
1823 if (!get_smart_mode () || !(mouse_mode == Editing::MouseObject) ) {
1824 set_mouse_mode (Editing::MouseRange, false);
1829 Editor::set_selection_from_punch()
1833 if ((location = _session->locations()->auto_punch_location()) == 0) {
1837 set_selection_from_range (*location);
1841 Editor::set_selection_from_loop()
1845 if ((location = _session->locations()->auto_loop_location()) == 0) {
1848 set_selection_from_range (*location);
1852 Editor::set_selection_from_range (Location& loc)
1854 begin_reversible_selection_op (X_("set selection from range"));
1856 selection->set (loc.start(), loc.end());
1858 // if no tracks are selected, enable all tracks
1859 // (_something_ has to be selected for any range selection, otherwise the user won't see anything)
1860 if (selection->tracks.empty()) {
1861 select_all_tracks();
1864 commit_reversible_selection_op ();
1866 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
1867 set_mouse_mode (MouseRange, false);
1872 Editor::select_all_selectables_using_time_selection ()
1874 list<Selectable *> touched;
1876 if (selection->time.empty()) {
1880 samplepos_t start = selection->time[clicked_selection].start;
1881 samplepos_t end = selection->time[clicked_selection].end;
1883 if (end - start < 1) {
1889 if (selection->tracks.empty()) {
1892 ts = &selection->tracks;
1895 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1896 if ((*iter)->hidden()) {
1899 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1902 begin_reversible_selection_op (X_("select all from range"));
1903 selection->set (touched);
1904 commit_reversible_selection_op ();
1909 Editor::select_all_selectables_using_punch()
1911 Location* location = _session->locations()->auto_punch_location();
1912 list<Selectable *> touched;
1914 if (location == 0 || (location->end() - location->start() <= 1)) {
1921 if (selection->tracks.empty()) {
1924 ts = &selection->tracks;
1927 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1928 if ((*iter)->hidden()) {
1931 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1933 begin_reversible_selection_op (X_("select all from punch"));
1934 selection->set (touched);
1935 commit_reversible_selection_op ();
1940 Editor::select_all_selectables_using_loop()
1942 Location* location = _session->locations()->auto_loop_location();
1943 list<Selectable *> touched;
1945 if (location == 0 || (location->end() - location->start() <= 1)) {
1952 if (selection->tracks.empty()) {
1955 ts = &selection->tracks;
1958 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1959 if ((*iter)->hidden()) {
1962 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1964 begin_reversible_selection_op (X_("select all from loop"));
1965 selection->set (touched);
1966 commit_reversible_selection_op ();
1971 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1975 list<Selectable *> touched;
1978 start = cursor->current_sample();
1979 end = _session->current_end_sample();
1981 if (cursor->current_sample() > 0) {
1983 end = cursor->current_sample() - 1;
1989 if (internal_editing()) {
1990 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1991 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1993 mrv->select_range (start, end);
2000 begin_reversible_selection_op (X_("select all after cursor"));
2002 begin_reversible_selection_op (X_("select all before cursor"));
2007 if (selection->tracks.empty()) {
2010 ts = &selection->tracks;
2013 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2014 if ((*iter)->hidden()) {
2017 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2019 selection->set (touched);
2020 commit_reversible_selection_op ();
2024 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
2028 list<Selectable *> touched;
2031 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
2032 end = _session->current_end_sample();
2034 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
2042 if (internal_editing()) {
2043 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2044 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2045 mrv->select_range (start, end);
2051 begin_reversible_selection_op (X_("select all after edit"));
2053 begin_reversible_selection_op (X_("select all before edit"));
2058 if (selection->tracks.empty()) {
2061 ts = &selection->tracks;
2064 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2065 if ((*iter)->hidden()) {
2068 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
2070 selection->set (touched);
2071 commit_reversible_selection_op ();
2075 Editor::select_all_selectables_between (bool within)
2079 list<Selectable *> touched;
2081 if (!get_edit_op_range (start, end)) {
2085 if (internal_editing()) {
2086 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2087 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
2088 mrv->select_range (start, end);
2095 if (selection->tracks.empty()) {
2098 ts = &selection->tracks;
2101 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
2102 if ((*iter)->hidden()) {
2105 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
2108 begin_reversible_selection_op (X_("Select all Selectables Between"));
2109 selection->set (touched);
2110 commit_reversible_selection_op ();
2114 Editor::select_range_between ()
2119 if (!selection->time.empty()) {
2120 selection->clear_time ();
2123 if (!get_edit_op_range (start, end)) {
2127 if (!get_smart_mode () || mouse_mode != Editing::MouseObject) {
2128 set_mouse_mode (MouseRange, false);
2131 begin_reversible_selection_op (X_("Select Range Between"));
2132 selection->set (start, end);
2133 commit_reversible_selection_op ();
2137 Editor::get_edit_op_range (samplepos_t& start, samplepos_t& end) const
2142 /* if an explicit range exists, use it */
2144 if ((mouse_mode == MouseRange || get_smart_mode()) && !selection->time.empty()) {
2145 /* we know that these are ordered */
2146 start = selection->time.start();
2147 end = selection->time.end_sample();
2155 // if (!mouse_sample (m, ignored)) {
2156 // /* mouse is not in a canvas, try playhead+selected marker.
2157 // this is probably most true when using menus.
2160 // if (selection->markers.empty()) {
2164 // start = selection->markers.front()->position();
2165 // end = _session->audible_sample();
2169 // switch (_edit_point) {
2170 // case EditAtPlayhead:
2171 // if (selection->markers.empty()) {
2172 // /* use mouse + playhead */
2174 // end = _session->audible_sample();
2176 // /* use playhead + selected marker */
2177 // start = _session->audible_sample();
2178 // end = selection->markers.front()->position();
2182 // case EditAtMouse:
2183 // /* use mouse + selected marker */
2184 // if (selection->markers.empty()) {
2186 // end = _session->audible_sample();
2188 // start = selection->markers.front()->position();
2193 // case EditAtSelectedMarker:
2194 // /* use mouse + selected marker */
2195 // if (selection->markers.empty()) {
2197 // MessageDialog win (_("No edit range defined"),
2202 // win.set_secondary_text (
2203 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2206 // win.set_default_response (RESPONSE_CLOSE);
2207 // win.set_position (Gtk::WIN_POS_MOUSE);
2212 // return false; // NO RANGE
2214 // start = selection->markers.front()->position();
2220 // if (start == end) {
2224 // if (start > end) {
2225 // swap (start, end);
2228 /* turn range into one delimited by start...end,
2238 Editor::deselect_all ()
2240 begin_reversible_selection_op (X_("Deselect All"));
2241 selection->clear ();
2242 commit_reversible_selection_op ();
2246 Editor::select_range (samplepos_t s, samplepos_t e)
2248 begin_reversible_selection_op (X_("Select Range"));
2249 selection->add (clicked_axisview);
2250 selection->time.clear ();
2251 long ret = selection->set (s, e);
2252 commit_reversible_selection_op ();