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"
25 #include "ardour/midi_region.h"
26 #include "ardour/playlist.h"
27 #include "ardour/profile.h"
28 #include "ardour/route_group.h"
29 #include "ardour/session.h"
31 #include "control_protocol/control_protocol.h"
33 #include "editor_drag.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "audio_streamview.h"
39 #include "automation_line.h"
40 #include "control_point.h"
41 #include "editor_regions.h"
42 #include "editor_cursors.h"
43 #include "midi_region_view.h"
49 using namespace ARDOUR;
53 using namespace Gtkmm2ext;
54 using namespace Editing;
56 struct TrackViewByPositionSorter
58 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
59 return a->y_position() < b->y_position();
64 Editor::extend_selection_to_track (TimeAxisView& view)
66 if (selection->selected (&view)) {
67 /* already selected, do nothing */
71 if (selection->tracks.empty()) {
73 if (!selection->selected (&view)) {
74 selection->set (&view);
81 /* something is already selected, so figure out which range of things to add */
83 TrackViewList to_be_added;
84 TrackViewList sorted = track_views;
85 TrackViewByPositionSorter cmp;
86 bool passed_clicked = false;
91 if (!selection->selected (&view)) {
92 to_be_added.push_back (&view);
95 /* figure out if we should go forward or backwards */
97 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
100 passed_clicked = true;
103 if (selection->selected (*i)) {
104 if (passed_clicked) {
113 passed_clicked = false;
117 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
120 passed_clicked = true;
124 if (passed_clicked) {
125 if ((*i)->hidden()) {
128 if (selection->selected (*i)) {
130 } else if (!(*i)->hidden()) {
131 to_be_added.push_back (*i);
138 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
141 passed_clicked = true;
145 if (passed_clicked) {
147 if ((*r)->hidden()) {
151 if (selection->selected (*r)) {
153 } else if (!(*r)->hidden()) {
154 to_be_added.push_back (*r);
160 if (!to_be_added.empty()) {
161 selection->add (to_be_added);
169 Editor::select_all_tracks ()
171 TrackViewList visible_views;
172 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
173 if ((*i)->marked_for_display()) {
174 visible_views.push_back (*i);
177 selection->set (visible_views);
180 /** Select clicked_axisview, unless there are no currently selected
181 * tracks, in which case nothing will happen unless `force' is true.
184 Editor::set_selected_track_as_side_effect (Selection::Operation op)
186 if (!clicked_axisview) {
190 RouteGroup* group = NULL;
191 if (clicked_routeview) {
192 group = clicked_routeview->route()->route_group();
195 bool had_tracks = !selection->tracks.empty();
196 RouteGroup& arg (_session->all_route_group());
199 case Selection::Toggle:
200 if (selection->selected (clicked_axisview)) {
201 if (arg.is_select() && arg.is_active()) {
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203 selection->remove(*i);
205 } else if (group && group->is_active()) {
206 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
207 if ((*i)->route_group() == group) {
208 selection->remove(*i);
212 selection->remove (clicked_axisview);
215 if (arg.is_select() && arg.is_active()) {
216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 } else if (group && group->is_active()) {
220 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
221 if ((*i)->route_group() == group) {
226 selection->add (clicked_axisview);
232 if (!had_tracks && arg.is_select() && arg.is_active()) {
233 /* nothing was selected already, and all group is active etc. so use
236 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 } else if (group && group->is_active()) {
240 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
241 if ((*i)->route_group() == group) {
246 selection->add (clicked_axisview);
252 if (!had_tracks && arg.is_select() && arg.is_active()) {
253 /* nothing was selected already, and all group is active etc. so use
256 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
259 } else if (group && group->is_active()) {
260 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
261 if ((*i)->route_group() == group) {
266 selection->set (clicked_axisview);
270 case Selection::Extend:
277 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
279 begin_reversible_selection_op (X_("Set Selected Track"));
282 case Selection::Toggle:
283 if (selection->selected (&view)) {
285 selection->remove (&view);
288 selection->add (&view);
293 if (!selection->selected (&view)) {
294 selection->add (&view);
299 selection->set (&view);
302 case Selection::Extend:
303 extend_selection_to_track (view);
307 commit_reversible_selection_op ();
311 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
313 if (!clicked_routeview) {
321 set_selected_track (*clicked_routeview, op, no_remove);
325 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
327 if (!clicked_control_point) {
335 selection->set (clicked_control_point);
341 selection->add (clicked_control_point);
345 case Selection::Toggle:
347 /* This is a bit of a hack; if we Primary-Click-Drag a control
348 point (for push drag) we want the point we clicked on to be
349 selected, otherwise we end up confusingly dragging an
350 unselected point. So here we ensure that the point is selected
351 after the press, and if we subsequently get a release (meaning no
352 drag occurred) we set things up so that the toggle has happened.
354 if (press && !selection->selected (clicked_control_point)) {
355 /* This is the button press, and the control point is not selected; make it so,
356 in case this press leads to a drag. Also note that having done this, we don't
357 need to toggle again on release.
359 selection->toggle (clicked_control_point);
360 _control_point_toggled_on_press = true;
362 } else if (!press && !_control_point_toggled_on_press) {
363 /* This is the release, and the point wasn't toggled on the press, so do it now */
364 selection->toggle (clicked_control_point);
368 _control_point_toggled_on_press = false;
371 case Selection::Extend:
380 Editor::get_onscreen_tracks (TrackViewList& tvl)
382 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
383 if ((*i)->y_position() < _visible_canvas_height) {
389 /** Call a slot for a given `basis' track and also for any track that is in the same
390 * active route group with a particular set of properties.
392 * @param sl Slot to call.
393 * @param basis Basis track.
394 * @param prop Properties that active edit groups must share to be included in the map.
398 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
400 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
402 if (route_basis == 0) {
406 set<RouteTimeAxisView*> tracks;
407 tracks.insert (route_basis);
409 RouteGroup* group = route_basis->route()->route_group();
411 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
413 /* the basis is a member of an active route group, with the appropriate
414 properties; find other members */
416 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
417 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
418 if (v && v->route()->route_group() == group) {
425 uint32_t const sz = tracks.size ();
427 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
432 /** Call a slot for a given `basis' track and also for any track that is in the same
433 * active route group with a particular set of properties.
435 * @param sl Slot to call.
436 * @param basis Basis track.
437 * @param prop Properties that active edit groups must share to be included in the map.
441 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
443 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
444 set<boost::shared_ptr<Playlist> > playlists;
446 if (route_basis == 0) {
450 set<RouteTimeAxisView*> tracks;
451 tracks.insert (route_basis);
453 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
455 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
457 /* the basis is a member of an active route group, with the appropriate
458 properties; find other members */
460 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
461 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
463 if (v && v->route()->route_group() == group) {
465 boost::shared_ptr<Track> t = v->track();
467 if (playlists.insert (t->playlist()).second) {
468 /* haven't seen this playlist yet */
472 /* not actually a "Track", but a timeaxis view that
473 we should mapover anyway.
482 uint32_t const sz = tracks.size ();
484 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
490 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
492 boost::shared_ptr<Playlist> pl;
493 vector<boost::shared_ptr<Region> > results;
495 boost::shared_ptr<Track> tr;
497 if ((tr = tv.track()) == 0) {
502 if (&tv == &basis->get_time_axis_view()) {
503 /* looking in same track as the original */
507 if ((pl = tr->playlist()) != 0) {
508 pl->get_equivalent_regions (basis->region(), results);
511 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
512 if ((marv = tv.view()->find_view (*ir)) != 0) {
513 all_equivs->push_back (marv);
519 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
521 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);
523 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
525 equivalent_regions.push_back (basis);
529 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
531 RegionSelection equivalent;
533 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
535 vector<RegionView*> eq;
537 mapover_tracks_with_unique_playlists (
538 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
539 &(*i)->get_time_axis_view(), prop);
541 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
552 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
554 int region_count = 0;
556 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
558 RouteTimeAxisView* tatv;
560 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
562 boost::shared_ptr<Playlist> pl;
563 vector<boost::shared_ptr<Region> > results;
565 boost::shared_ptr<Track> tr;
567 if ((tr = tatv->track()) == 0) {
572 if ((pl = (tr->playlist())) != 0) {
573 pl->get_region_list_equivalent_regions (region, results);
576 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
577 if ((marv = tatv->view()->find_view (*ir)) != 0) {
590 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
592 vector<RegionView*> all_equivalent_regions;
595 if (!clicked_regionview || !clicked_routeview) {
600 button_release_can_deselect = false;
603 if (op == Selection::Toggle || op == Selection::Set) {
606 case Selection::Toggle:
607 if (selection->selected (clicked_regionview)) {
610 /* whatever was clicked was selected already; do nothing here but allow
611 the button release to deselect it
614 button_release_can_deselect = true;
617 if (button_release_can_deselect) {
619 /* just remove this one region, but only on a permitted button release */
621 selection->remove (clicked_regionview);
624 /* no more deselect action on button release till a new press
625 finds an already selected object.
628 button_release_can_deselect = false;
636 if (selection->selected (clicked_routeview)) {
637 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
639 all_equivalent_regions.push_back (clicked_regionview);
642 /* add all the equivalent regions, but only on button press */
644 if (!all_equivalent_regions.empty()) {
648 selection->add (all_equivalent_regions);
654 if (!selection->selected (clicked_regionview)) {
655 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
656 selection->set (all_equivalent_regions);
659 /* clicked on an already selected region */
663 if (selection->regions.size() > 1) {
664 /* collapse region selection down to just this one region (and its equivalents) */
665 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
666 selection->set(all_equivalent_regions);
678 } else if (op == Selection::Extend) {
680 list<Selectable*> results;
681 framepos_t last_frame;
682 framepos_t first_frame;
683 bool same_track = false;
685 /* 1. find the last selected regionview in the track that was clicked in */
688 first_frame = max_framepos;
690 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
691 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
693 if ((*x)->region()->last_frame() > last_frame) {
694 last_frame = (*x)->region()->last_frame();
697 if ((*x)->region()->first_frame() < first_frame) {
698 first_frame = (*x)->region()->first_frame();
707 /* 2. figure out the boundaries for our search for new objects */
709 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
710 case Evoral::OverlapNone:
711 if (last_frame < clicked_regionview->region()->first_frame()) {
712 first_frame = last_frame;
713 last_frame = clicked_regionview->region()->last_frame();
715 last_frame = first_frame;
716 first_frame = clicked_regionview->region()->first_frame();
720 case Evoral::OverlapExternal:
721 if (last_frame < clicked_regionview->region()->first_frame()) {
722 first_frame = last_frame;
723 last_frame = clicked_regionview->region()->last_frame();
725 last_frame = first_frame;
726 first_frame = clicked_regionview->region()->first_frame();
730 case Evoral::OverlapInternal:
731 if (last_frame < clicked_regionview->region()->first_frame()) {
732 first_frame = last_frame;
733 last_frame = clicked_regionview->region()->last_frame();
735 last_frame = first_frame;
736 first_frame = clicked_regionview->region()->first_frame();
740 case Evoral::OverlapStart:
741 case Evoral::OverlapEnd:
742 /* nothing to do except add clicked region to selection, since it
743 overlaps with the existing selection in this track.
750 /* click in a track that has no regions selected, so extend vertically
751 to pick out all regions that are defined by the existing selection
756 first_frame = clicked_regionview->region()->position();
757 last_frame = clicked_regionview->region()->last_frame();
759 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
760 if ((*i)->region()->position() < first_frame) {
761 first_frame = (*i)->region()->position();
763 if ((*i)->region()->last_frame() + 1 > last_frame) {
764 last_frame = (*i)->region()->last_frame();
769 /* 2. find all the tracks we should select in */
771 set<RouteTimeAxisView*> relevant_tracks;
773 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
774 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
776 relevant_tracks.insert (r);
780 set<RouteTimeAxisView*> already_in_selection;
782 if (relevant_tracks.empty()) {
784 /* no tracks selected .. thus .. if the
785 regionview we're in isn't selected
786 (i.e. we're about to extend to it), then
787 find all tracks between the this one and
791 if (!selection->selected (clicked_regionview)) {
793 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
797 /* add this track to the ones we will search */
799 relevant_tracks.insert (rtv);
801 /* find the track closest to this one that
802 already a selected region.
805 RouteTimeAxisView* closest = 0;
806 int distance = INT_MAX;
807 int key = rtv->route()->order_key ();
809 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
811 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
813 if (artv && artv != rtv) {
815 pair<set<RouteTimeAxisView*>::iterator,bool> result;
817 result = already_in_selection.insert (artv);
820 /* newly added to already_in_selection */
822 int d = artv->route()->order_key ();
826 if (abs (d) < distance) {
836 /* now add all tracks between that one and this one */
838 int okey = closest->route()->order_key ();
844 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
845 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
846 if (artv && artv != rtv) {
848 int k = artv->route()->order_key ();
850 if (k >= okey && k <= key) {
852 /* in range but don't add it if
853 it already has tracks selected.
854 this avoids odd selection
855 behaviour that feels wrong.
858 if (find (already_in_selection.begin(),
859 already_in_selection.end(),
860 artv) == already_in_selection.end()) {
862 relevant_tracks.insert (artv);
872 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
873 one that was clicked.
876 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
877 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
880 /* 4. convert to a vector of regions */
882 vector<RegionView*> regions;
884 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
887 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
888 regions.push_back (arv);
892 if (!regions.empty()) {
893 selection->add (regions);
904 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
906 vector<RegionView*> all_equivalent_regions;
908 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
910 if (all_equivalent_regions.empty()) {
914 begin_reversible_selection_op (X_("set selected regions"));
917 case Selection::Toggle:
918 /* XXX this is not correct */
919 selection->toggle (all_equivalent_regions);
922 selection->set (all_equivalent_regions);
924 case Selection::Extend:
925 selection->add (all_equivalent_regions);
928 selection->add (all_equivalent_regions);
932 commit_reversible_selection_op () ;
936 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
939 boost::shared_ptr<Region> r (weak_r.lock());
945 if ((rv = sv->find_view (r)) == 0) {
949 /* don't reset the selection if its something other than
950 a single other region.
953 if (selection->regions.size() > 1) {
957 begin_reversible_selection_op (X_("set selected regions"));
961 commit_reversible_selection_op () ;
967 Editor::track_selection_changed ()
969 switch (selection->tracks.size()) {
973 set_selected_mixer_strip (*(selection->tracks.front()));
977 RouteNotificationListPtr routes (new RouteNotificationList);
979 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
981 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
983 (*i)->set_selected (yn);
985 TimeAxisView::Children c = (*i)->get_child_list ();
986 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
987 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
991 (*i)->reshow_selection (selection->time);
993 (*i)->hide_selection ();
998 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1000 routes->push_back (rtav->route());
1005 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1007 /* notify control protocols */
1009 ControlProtocol::TrackSelectionChanged (routes);
1011 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1012 uint32_t audio_track_cnt = 0;
1013 uint32_t midi_track_cnt = 0;
1015 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1016 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1019 if (atv->is_audio_track()) {
1024 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1027 if (mtv->is_midi_track()) {
1034 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1039 Editor::time_selection_changed ()
1041 if (Profile->get_sae()) {
1045 /* XXX this is superficially inefficient. Hide the selection in all
1046 * tracks, then show it in all selected tracks.
1048 * However, if you investigate what this actually does, it isn't
1049 * anywhere nearly as bad as it may appear. Remember: nothing is
1050 * redrawn or even recomputed during these two loops - that only
1051 * happens when we next render ...
1054 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1055 (*i)->hide_selection ();
1058 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1059 (*i)->show_selection (selection->time);
1062 if (selection->time.empty()) {
1063 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1065 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1068 /* propagate into backend, but only when there is no drag or we are at
1069 * the end of a drag, otherwise this is too expensive (could case a
1070 * locate per mouse motion event.
1073 if (_session && !_drags->active()) {
1074 if (selection->time.length() != 0) {
1075 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1077 _session->clear_range_selection ();
1082 /** Set all region actions to have a given sensitivity */
1084 Editor::sensitize_all_region_actions (bool s)
1086 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1088 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1089 (*i)->set_sensitive (s);
1092 _all_region_actions_sensitized = s;
1095 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1096 * This method should be called just before displaying a Region menu. When a Region menu is not
1097 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1098 * on entered_regionviews work without having to check sensitivity every time the selection or
1099 * entered_regionview changes.
1101 * This method also sets up toggle action state as appropriate.
1104 Editor::sensitize_the_right_region_actions ()
1107 RegionSelection rs = get_regions_from_selection_and_entered ();
1108 sensitize_all_region_actions (!rs.empty ());
1110 _ignore_region_action = true;
1112 /* Look through the regions that are selected and make notes about what we have got */
1114 bool have_audio = false;
1115 bool have_multichannel_audio = false;
1116 bool have_midi = false;
1117 bool have_locked = false;
1118 bool have_unlocked = false;
1119 bool have_video_locked = false;
1120 bool have_video_unlocked = false;
1121 bool have_position_lock_style_audio = false;
1122 bool have_position_lock_style_music = false;
1123 bool have_muted = false;
1124 bool have_unmuted = false;
1125 bool have_opaque = false;
1126 bool have_non_opaque = false;
1127 bool have_not_at_natural_position = false;
1128 bool have_envelope_active = false;
1129 bool have_envelope_inactive = false;
1130 bool have_non_unity_scale_amplitude = false;
1131 bool have_compound_regions = false;
1132 bool have_inactive_fade_in = false;
1133 bool have_inactive_fade_out = false;
1134 bool have_active_fade_in = false;
1135 bool have_active_fade_out = false;
1137 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1139 boost::shared_ptr<Region> r = (*i)->region ();
1140 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1144 if (ar->n_channels() > 1) {
1145 have_multichannel_audio = true;
1149 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1153 if (r->is_compound()) {
1154 have_compound_regions = true;
1160 have_unlocked = true;
1163 if (r->video_locked()) {
1164 have_video_locked = true;
1166 have_video_unlocked = true;
1169 if (r->position_lock_style() == MusicTime) {
1170 have_position_lock_style_music = true;
1172 have_position_lock_style_audio = true;
1178 have_unmuted = true;
1184 have_non_opaque = true;
1187 if (!r->at_natural_position()) {
1188 have_not_at_natural_position = true;
1192 if (ar->envelope_active()) {
1193 have_envelope_active = true;
1195 have_envelope_inactive = true;
1198 if (ar->scale_amplitude() != 1) {
1199 have_non_unity_scale_amplitude = true;
1202 if (ar->fade_in_active ()) {
1203 have_active_fade_in = true;
1205 have_inactive_fade_in = true;
1208 if (ar->fade_out_active ()) {
1209 have_active_fade_out = true;
1211 have_inactive_fade_out = true;
1216 if (rs.size() > 1) {
1217 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1218 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1219 _region_actions->get_action("rename-region")->set_sensitive (false);
1221 /* XXX need to check whether there is than 1 per
1222 playlist, because otherwise this makes no sense.
1224 _region_actions->get_action("combine-regions")->set_sensitive (true);
1226 _region_actions->get_action("combine-regions")->set_sensitive (false);
1228 } else if (rs.size() == 1) {
1229 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1230 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1231 _region_actions->get_action("combine-regions")->set_sensitive (false);
1234 if (!have_multichannel_audio) {
1235 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1239 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1240 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1241 _region_actions->get_action("quantize-region")->set_sensitive (false);
1242 _region_actions->get_action("legatize-region")->set_sensitive (false);
1243 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1244 _region_actions->get_action("fork-region")->set_sensitive (false);
1245 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1246 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1247 _region_actions->get_action("transpose-region")->set_sensitive (false);
1249 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1250 /* others were already marked sensitive */
1253 if (_edit_point == EditAtMouse) {
1254 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1255 _region_actions->get_action("trim-front")->set_sensitive (false);
1256 _region_actions->get_action("trim-back")->set_sensitive (false);
1257 _region_actions->get_action("split-region")->set_sensitive (false);
1258 _region_actions->get_action("place-transient")->set_sensitive (false);
1261 if (have_compound_regions) {
1262 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1264 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1269 if (have_envelope_active && !have_envelope_inactive) {
1270 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1271 } else if (have_envelope_active && have_envelope_inactive) {
1272 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1277 _region_actions->get_action("analyze-region")->set_sensitive (false);
1278 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1279 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1280 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1281 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1282 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1286 if (!have_non_unity_scale_amplitude || !have_audio) {
1287 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1290 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1291 a->set_active (have_locked && !have_unlocked);
1292 if (have_locked && have_unlocked) {
1293 // a->set_inconsistent ();
1296 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1297 a->set_active (have_video_locked && !have_video_unlocked);
1298 if (have_video_locked && have_video_unlocked) {
1299 // a->set_inconsistent ();
1302 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1303 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1305 if (have_position_lock_style_music && have_position_lock_style_audio) {
1306 // a->set_inconsistent ();
1309 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1310 a->set_active (have_muted && !have_unmuted);
1311 if (have_muted && have_unmuted) {
1312 // a->set_inconsistent ();
1315 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1316 a->set_active (have_opaque && !have_non_opaque);
1317 if (have_opaque && have_non_opaque) {
1318 // a->set_inconsistent ();
1321 if (!have_not_at_natural_position) {
1322 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1325 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1326 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1327 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1329 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1332 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1333 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1334 if (have_active_fade_in && have_inactive_fade_in) {
1335 // a->set_inconsistent ();
1338 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1339 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1341 if (have_active_fade_out && have_inactive_fade_out) {
1342 // a->set_inconsistent ();
1345 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1346 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1348 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1349 a->set_active (have_active_fade && !have_inactive_fade);
1351 if (have_active_fade && have_inactive_fade) {
1352 // a->set_inconsistent ();
1355 _ignore_region_action = false;
1357 _all_region_actions_sensitized = false;
1362 Editor::region_selection_changed ()
1364 _regions->block_change_connection (true);
1365 editor_regions_selection_changed_connection.block(true);
1367 if (_region_selection_change_updates_region_list) {
1368 _regions->unselect_all ();
1371 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1372 (*i)->set_selected_regionviews (selection->regions);
1375 if (_region_selection_change_updates_region_list) {
1376 _regions->set_selected (selection->regions);
1379 _regions->block_change_connection (false);
1380 editor_regions_selection_changed_connection.block(false);
1382 if (!_all_region_actions_sensitized) {
1383 /* This selection change might have changed what region actions
1384 are allowed, so sensitize them all in case a key is pressed.
1386 sensitize_all_region_actions (true);
1389 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1390 maybe_locate_with_edit_preroll (selection->regions.start());
1393 /* propagate into backend */
1396 if (!selection->regions.empty()) {
1397 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1399 _session->clear_object_selection ();
1406 Editor::point_selection_changed ()
1408 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1409 (*i)->set_selected_points (selection->points);
1414 Editor::select_all_in_track (Selection::Operation op)
1416 list<Selectable *> touched;
1418 if (!clicked_routeview) {
1422 begin_reversible_selection_op (X_("Select All in Track"));
1424 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1427 case Selection::Toggle:
1428 selection->add (touched);
1430 case Selection::Set:
1431 selection->set (touched);
1433 case Selection::Extend:
1434 /* meaningless, because we're selecting everything */
1436 case Selection::Add:
1437 selection->add (touched);
1441 commit_reversible_selection_op ();
1445 Editor::select_all_internal_edit (Selection::Operation)
1447 bool selected = false;
1449 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1450 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1452 mrv->select_all_notes ();
1457 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1459 mrv->select_all_notes ();
1467 Editor::select_all_objects (Selection::Operation op)
1469 list<Selectable *> touched;
1471 TrackViewList ts = track_views;
1473 if (internal_editing() && select_all_internal_edit(op)) {
1474 return; // Selected notes
1477 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1478 if ((*iter)->hidden()) {
1481 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1482 selection->add (*iter);
1486 begin_reversible_selection_op (X_("select all"));
1488 case Selection::Add:
1489 selection->add (touched);
1491 case Selection::Toggle:
1492 selection->add (touched);
1494 case Selection::Set:
1495 selection->set (touched);
1497 case Selection::Extend:
1498 /* meaningless, because we're selecting everything */
1501 commit_reversible_selection_op ();
1505 Editor::invert_selection_in_track ()
1507 list<Selectable *> touched;
1509 if (!clicked_routeview) {
1513 begin_reversible_selection_op (X_("Invert Selection in Track"));
1514 clicked_routeview->get_inverted_selectables (*selection, touched);
1515 selection->set (touched);
1516 commit_reversible_selection_op ();
1520 Editor::invert_selection ()
1522 list<Selectable *> touched;
1524 if (internal_editing()) {
1525 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1526 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1528 mrv->invert_selection ();
1534 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1535 if ((*iter)->hidden()) {
1538 (*iter)->get_inverted_selectables (*selection, touched);
1541 begin_reversible_selection_op (X_("Invert Selection"));
1542 selection->set (touched);
1543 commit_reversible_selection_op ();
1546 /** @param start Start time in session frames.
1547 * @param end End time in session frames.
1548 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1549 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1550 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1551 * within the region are already selected.
1554 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1556 list<Selectable*> found;
1558 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1560 if ((*iter)->hidden()) {
1564 (*iter)->get_selectables (start, end, top, bot, found);
1567 if (found.empty()) {
1568 selection->clear_objects();
1569 selection->clear_time ();
1573 if (preserve_if_selected && op != Selection::Toggle) {
1574 list<Selectable*>::iterator i = found.begin();
1575 while (i != found.end() && (*i)->get_selected()) {
1579 if (i == found.end()) {
1584 begin_reversible_selection_op (X_("select all within"));
1586 case Selection::Add:
1587 selection->add (found);
1589 case Selection::Toggle:
1590 selection->toggle (found);
1592 case Selection::Set:
1593 selection->set (found);
1595 case Selection::Extend:
1596 /* not defined yet */
1600 commit_reversible_selection_op ();
1604 Editor::set_selection_from_region ()
1606 if (selection->regions.empty()) {
1610 /* find all the tracks that have selected regions */
1612 set<TimeAxisView*> tracks;
1614 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1615 tracks.insert (&(*r)->get_time_axis_view());
1619 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1621 /* select range (this will clear the region selection) */
1623 selection->set (selection->regions.start(), selection->regions.end_frame());
1625 /* and select the tracks */
1627 selection->set (tvl);
1629 if (!Profile->get_sae()) {
1630 set_mouse_mode (Editing::MouseRange, false);
1635 Editor::set_selection_from_punch()
1639 if ((location = _session->locations()->auto_punch_location()) == 0) {
1643 set_selection_from_range (*location);
1647 Editor::set_selection_from_loop()
1651 if ((location = _session->locations()->auto_loop_location()) == 0) {
1654 set_selection_from_range (*location);
1658 Editor::set_selection_from_range (Location& loc)
1660 begin_reversible_selection_op (X_("set selection from range"));
1661 selection->set (loc.start(), loc.end());
1662 commit_reversible_selection_op ();
1664 if (!Profile->get_sae()) {
1665 set_mouse_mode (Editing::MouseRange, false);
1670 Editor::select_all_selectables_using_time_selection ()
1672 list<Selectable *> touched;
1674 if (selection->time.empty()) {
1678 framepos_t start = selection->time[clicked_selection].start;
1679 framepos_t end = selection->time[clicked_selection].end;
1681 if (end - start < 1) {
1687 if (selection->tracks.empty()) {
1690 ts = &selection->tracks;
1693 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1694 if ((*iter)->hidden()) {
1697 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1700 begin_reversible_selection_op (X_("select all from range"));
1701 selection->set (touched);
1702 commit_reversible_selection_op ();
1707 Editor::select_all_selectables_using_punch()
1709 Location* location = _session->locations()->auto_punch_location();
1710 list<Selectable *> touched;
1712 if (location == 0 || (location->end() - location->start() <= 1)) {
1719 if (selection->tracks.empty()) {
1722 ts = &selection->tracks;
1725 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1726 if ((*iter)->hidden()) {
1729 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1731 begin_reversible_selection_op (X_("select all from punch"));
1732 selection->set (touched);
1733 commit_reversible_selection_op ();
1738 Editor::select_all_selectables_using_loop()
1740 Location* location = _session->locations()->auto_loop_location();
1741 list<Selectable *> touched;
1743 if (location == 0 || (location->end() - location->start() <= 1)) {
1750 if (selection->tracks.empty()) {
1753 ts = &selection->tracks;
1756 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1757 if ((*iter)->hidden()) {
1760 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1762 begin_reversible_selection_op (X_("select all from loop"));
1763 selection->set (touched);
1764 commit_reversible_selection_op ();
1769 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1773 list<Selectable *> touched;
1776 start = cursor->current_frame();
1777 end = _session->current_end_frame();
1779 if (cursor->current_frame() > 0) {
1781 end = cursor->current_frame() - 1;
1787 if (internal_editing()) {
1788 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1789 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1791 mrv->select_range (start, end);
1798 begin_reversible_selection_op (X_("select all after cursor"));
1800 begin_reversible_selection_op (X_("select all before cursor"));
1805 if (selection->tracks.empty()) {
1808 ts = &selection->tracks;
1811 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1812 if ((*iter)->hidden()) {
1815 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1817 selection->set (touched);
1818 commit_reversible_selection_op ();
1822 Editor::select_all_selectables_using_edit (bool after)
1826 list<Selectable *> touched;
1829 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1830 end = _session->current_end_frame();
1832 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1840 if (internal_editing()) {
1841 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1842 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1843 mrv->select_range (start, end);
1849 begin_reversible_selection_op (X_("select all after edit"));
1851 begin_reversible_selection_op (X_("select all before edit"));
1856 if (selection->tracks.empty()) {
1859 ts = &selection->tracks;
1862 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1863 if ((*iter)->hidden()) {
1866 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1868 selection->set (touched);
1869 commit_reversible_selection_op ();
1873 Editor::select_all_selectables_between (bool within)
1877 list<Selectable *> touched;
1879 if (!get_edit_op_range (start, end)) {
1883 if (internal_editing()) {
1884 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1885 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1886 mrv->select_range (start, end);
1893 if (selection->tracks.empty()) {
1896 ts = &selection->tracks;
1899 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1900 if ((*iter)->hidden()) {
1903 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1906 begin_reversible_selection_op (X_("Select all Selectables Between"));
1907 selection->set (touched);
1908 commit_reversible_selection_op ();
1912 Editor::select_range_between ()
1917 if ( !selection->time.empty() ) {
1918 selection->clear_time ();
1921 if (!get_edit_op_range (start, end)) {
1925 begin_reversible_selection_op (X_("Select Range Between"));
1926 set_mouse_mode (MouseRange);
1927 selection->set (start, end);
1928 commit_reversible_selection_op ();
1932 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1937 /* if an explicit range exists, use it */
1939 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1940 /* we know that these are ordered */
1941 start = selection->time.start();
1942 end = selection->time.end_frame();
1950 // if (!mouse_frame (m, ignored)) {
1951 // /* mouse is not in a canvas, try playhead+selected marker.
1952 // this is probably most true when using menus.
1955 // if (selection->markers.empty()) {
1959 // start = selection->markers.front()->position();
1960 // end = _session->audible_frame();
1964 // switch (_edit_point) {
1965 // case EditAtPlayhead:
1966 // if (selection->markers.empty()) {
1967 // /* use mouse + playhead */
1969 // end = _session->audible_frame();
1971 // /* use playhead + selected marker */
1972 // start = _session->audible_frame();
1973 // end = selection->markers.front()->position();
1977 // case EditAtMouse:
1978 // /* use mouse + selected marker */
1979 // if (selection->markers.empty()) {
1981 // end = _session->audible_frame();
1983 // start = selection->markers.front()->position();
1988 // case EditAtSelectedMarker:
1989 // /* use mouse + selected marker */
1990 // if (selection->markers.empty()) {
1992 // MessageDialog win (_("No edit range defined"),
1997 // win.set_secondary_text (
1998 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2001 // win.set_default_response (RESPONSE_CLOSE);
2002 // win.set_position (Gtk::WIN_POS_MOUSE);
2007 // return false; // NO RANGE
2009 // start = selection->markers.front()->position();
2015 // if (start == end) {
2019 // if (start > end) {
2020 // swap (start, end);
2023 /* turn range into one delimited by start...end,
2033 Editor::deselect_all ()
2035 begin_reversible_selection_op (X_("Deselect All"));
2036 selection->clear ();
2037 commit_reversible_selection_op ();
2041 Editor::select_range (framepos_t s, framepos_t e)
2043 begin_reversible_selection_op (X_("Select Range"));
2044 selection->add (clicked_axisview);
2045 selection->time.clear ();
2046 long ret = selection->set (s, e);
2047 commit_reversible_selection_op ();