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("transform-region")->set_sensitive (false);
1245 _region_actions->get_action("fork-region")->set_sensitive (false);
1246 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1247 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1248 _region_actions->get_action("transpose-region")->set_sensitive (false);
1250 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1251 /* others were already marked sensitive */
1254 if (_edit_point == EditAtMouse) {
1255 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1256 _region_actions->get_action("trim-front")->set_sensitive (false);
1257 _region_actions->get_action("trim-back")->set_sensitive (false);
1258 _region_actions->get_action("split-region")->set_sensitive (false);
1259 _region_actions->get_action("place-transient")->set_sensitive (false);
1262 if (have_compound_regions) {
1263 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1265 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1270 if (have_envelope_active && !have_envelope_inactive) {
1271 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1272 } else if (have_envelope_active && have_envelope_inactive) {
1273 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1278 _region_actions->get_action("analyze-region")->set_sensitive (false);
1279 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1280 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1281 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1282 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1283 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1287 if (!have_non_unity_scale_amplitude || !have_audio) {
1288 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1291 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1292 a->set_active (have_locked && !have_unlocked);
1293 if (have_locked && have_unlocked) {
1294 // a->set_inconsistent ();
1297 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1298 a->set_active (have_video_locked && !have_video_unlocked);
1299 if (have_video_locked && have_video_unlocked) {
1300 // a->set_inconsistent ();
1303 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1304 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1306 if (have_position_lock_style_music && have_position_lock_style_audio) {
1307 // a->set_inconsistent ();
1310 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1311 a->set_active (have_muted && !have_unmuted);
1312 if (have_muted && have_unmuted) {
1313 // a->set_inconsistent ();
1316 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1317 a->set_active (have_opaque && !have_non_opaque);
1318 if (have_opaque && have_non_opaque) {
1319 // a->set_inconsistent ();
1322 if (!have_not_at_natural_position) {
1323 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1326 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1327 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1328 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1330 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1333 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1334 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1335 if (have_active_fade_in && have_inactive_fade_in) {
1336 // a->set_inconsistent ();
1339 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1340 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1342 if (have_active_fade_out && have_inactive_fade_out) {
1343 // a->set_inconsistent ();
1346 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1347 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1349 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1350 a->set_active (have_active_fade && !have_inactive_fade);
1352 if (have_active_fade && have_inactive_fade) {
1353 // a->set_inconsistent ();
1356 _ignore_region_action = false;
1358 _all_region_actions_sensitized = false;
1363 Editor::region_selection_changed ()
1365 _regions->block_change_connection (true);
1366 editor_regions_selection_changed_connection.block(true);
1368 if (_region_selection_change_updates_region_list) {
1369 _regions->unselect_all ();
1372 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1373 (*i)->set_selected_regionviews (selection->regions);
1376 if (_region_selection_change_updates_region_list) {
1377 _regions->set_selected (selection->regions);
1380 _regions->block_change_connection (false);
1381 editor_regions_selection_changed_connection.block(false);
1383 if (!_all_region_actions_sensitized) {
1384 /* This selection change might have changed what region actions
1385 are allowed, so sensitize them all in case a key is pressed.
1387 sensitize_all_region_actions (true);
1390 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1391 maybe_locate_with_edit_preroll (selection->regions.start());
1394 /* propagate into backend */
1397 if (!selection->regions.empty()) {
1398 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1400 _session->clear_object_selection ();
1407 Editor::point_selection_changed ()
1409 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1410 (*i)->set_selected_points (selection->points);
1415 Editor::select_all_in_track (Selection::Operation op)
1417 list<Selectable *> touched;
1419 if (!clicked_routeview) {
1423 begin_reversible_selection_op (X_("Select All in Track"));
1425 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1428 case Selection::Toggle:
1429 selection->add (touched);
1431 case Selection::Set:
1432 selection->set (touched);
1434 case Selection::Extend:
1435 /* meaningless, because we're selecting everything */
1437 case Selection::Add:
1438 selection->add (touched);
1442 commit_reversible_selection_op ();
1446 Editor::select_all_internal_edit (Selection::Operation)
1448 bool selected = false;
1450 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1451 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1453 mrv->select_all_notes ();
1458 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1460 mrv->select_all_notes ();
1468 Editor::select_all_objects (Selection::Operation op)
1470 list<Selectable *> touched;
1472 TrackViewList ts = track_views;
1474 if (internal_editing() && select_all_internal_edit(op)) {
1475 return; // Selected notes
1478 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1479 if ((*iter)->hidden()) {
1482 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1483 selection->add (*iter);
1487 begin_reversible_selection_op (X_("select all"));
1489 case Selection::Add:
1490 selection->add (touched);
1492 case Selection::Toggle:
1493 selection->add (touched);
1495 case Selection::Set:
1496 selection->set (touched);
1498 case Selection::Extend:
1499 /* meaningless, because we're selecting everything */
1502 commit_reversible_selection_op ();
1506 Editor::invert_selection_in_track ()
1508 list<Selectable *> touched;
1510 if (!clicked_routeview) {
1514 begin_reversible_selection_op (X_("Invert Selection in Track"));
1515 clicked_routeview->get_inverted_selectables (*selection, touched);
1516 selection->set (touched);
1517 commit_reversible_selection_op ();
1521 Editor::invert_selection ()
1523 list<Selectable *> touched;
1525 if (internal_editing()) {
1526 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1527 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1529 mrv->invert_selection ();
1535 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1536 if ((*iter)->hidden()) {
1539 (*iter)->get_inverted_selectables (*selection, touched);
1542 begin_reversible_selection_op (X_("Invert Selection"));
1543 selection->set (touched);
1544 commit_reversible_selection_op ();
1547 /** @param start Start time in session frames.
1548 * @param end End time in session frames.
1549 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1550 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1551 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1552 * within the region are already selected.
1555 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1557 list<Selectable*> found;
1559 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1561 if ((*iter)->hidden()) {
1565 (*iter)->get_selectables (start, end, top, bot, found);
1568 if (found.empty()) {
1569 selection->clear_objects();
1570 selection->clear_time ();
1574 if (preserve_if_selected && op != Selection::Toggle) {
1575 list<Selectable*>::iterator i = found.begin();
1576 while (i != found.end() && (*i)->get_selected()) {
1580 if (i == found.end()) {
1585 begin_reversible_selection_op (X_("select all within"));
1587 case Selection::Add:
1588 selection->add (found);
1590 case Selection::Toggle:
1591 selection->toggle (found);
1593 case Selection::Set:
1594 selection->set (found);
1596 case Selection::Extend:
1597 /* not defined yet */
1601 commit_reversible_selection_op ();
1605 Editor::set_selection_from_region ()
1607 if (selection->regions.empty()) {
1611 /* find all the tracks that have selected regions */
1613 set<TimeAxisView*> tracks;
1615 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1616 tracks.insert (&(*r)->get_time_axis_view());
1620 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1622 /* select range (this will clear the region selection) */
1624 selection->set (selection->regions.start(), selection->regions.end_frame());
1626 /* and select the tracks */
1628 selection->set (tvl);
1630 if (!Profile->get_sae()) {
1631 set_mouse_mode (Editing::MouseRange, false);
1636 Editor::set_selection_from_punch()
1640 if ((location = _session->locations()->auto_punch_location()) == 0) {
1644 set_selection_from_range (*location);
1648 Editor::set_selection_from_loop()
1652 if ((location = _session->locations()->auto_loop_location()) == 0) {
1655 set_selection_from_range (*location);
1659 Editor::set_selection_from_range (Location& loc)
1661 begin_reversible_selection_op (X_("set selection from range"));
1662 selection->set (loc.start(), loc.end());
1663 commit_reversible_selection_op ();
1665 if (!Profile->get_sae()) {
1666 set_mouse_mode (Editing::MouseRange, false);
1671 Editor::select_all_selectables_using_time_selection ()
1673 list<Selectable *> touched;
1675 if (selection->time.empty()) {
1679 framepos_t start = selection->time[clicked_selection].start;
1680 framepos_t end = selection->time[clicked_selection].end;
1682 if (end - start < 1) {
1688 if (selection->tracks.empty()) {
1691 ts = &selection->tracks;
1694 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1695 if ((*iter)->hidden()) {
1698 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1701 begin_reversible_selection_op (X_("select all from range"));
1702 selection->set (touched);
1703 commit_reversible_selection_op ();
1708 Editor::select_all_selectables_using_punch()
1710 Location* location = _session->locations()->auto_punch_location();
1711 list<Selectable *> touched;
1713 if (location == 0 || (location->end() - location->start() <= 1)) {
1720 if (selection->tracks.empty()) {
1723 ts = &selection->tracks;
1726 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1727 if ((*iter)->hidden()) {
1730 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1732 begin_reversible_selection_op (X_("select all from punch"));
1733 selection->set (touched);
1734 commit_reversible_selection_op ();
1739 Editor::select_all_selectables_using_loop()
1741 Location* location = _session->locations()->auto_loop_location();
1742 list<Selectable *> touched;
1744 if (location == 0 || (location->end() - location->start() <= 1)) {
1751 if (selection->tracks.empty()) {
1754 ts = &selection->tracks;
1757 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1758 if ((*iter)->hidden()) {
1761 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1763 begin_reversible_selection_op (X_("select all from loop"));
1764 selection->set (touched);
1765 commit_reversible_selection_op ();
1770 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1774 list<Selectable *> touched;
1777 start = cursor->current_frame();
1778 end = _session->current_end_frame();
1780 if (cursor->current_frame() > 0) {
1782 end = cursor->current_frame() - 1;
1788 if (internal_editing()) {
1789 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1790 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1792 mrv->select_range (start, end);
1799 begin_reversible_selection_op (X_("select all after cursor"));
1801 begin_reversible_selection_op (X_("select all before cursor"));
1806 if (selection->tracks.empty()) {
1809 ts = &selection->tracks;
1812 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1813 if ((*iter)->hidden()) {
1816 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1818 selection->set (touched);
1819 commit_reversible_selection_op ();
1823 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1827 list<Selectable *> touched;
1830 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1831 end = _session->current_end_frame();
1833 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1841 if (internal_editing()) {
1842 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1843 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1844 mrv->select_range (start, end);
1850 begin_reversible_selection_op (X_("select all after edit"));
1852 begin_reversible_selection_op (X_("select all before edit"));
1857 if (selection->tracks.empty()) {
1860 ts = &selection->tracks;
1863 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1864 if ((*iter)->hidden()) {
1867 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1869 selection->set (touched);
1870 commit_reversible_selection_op ();
1874 Editor::select_all_selectables_between (bool within)
1878 list<Selectable *> touched;
1880 if (!get_edit_op_range (start, end)) {
1884 if (internal_editing()) {
1885 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1886 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1887 mrv->select_range (start, end);
1894 if (selection->tracks.empty()) {
1897 ts = &selection->tracks;
1900 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1901 if ((*iter)->hidden()) {
1904 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1907 begin_reversible_selection_op (X_("Select all Selectables Between"));
1908 selection->set (touched);
1909 commit_reversible_selection_op ();
1913 Editor::select_range_between ()
1918 if ( !selection->time.empty() ) {
1919 selection->clear_time ();
1922 if (!get_edit_op_range (start, end)) {
1926 begin_reversible_selection_op (X_("Select Range Between"));
1927 set_mouse_mode (MouseRange);
1928 selection->set (start, end);
1929 commit_reversible_selection_op ();
1933 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1938 /* if an explicit range exists, use it */
1940 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1941 /* we know that these are ordered */
1942 start = selection->time.start();
1943 end = selection->time.end_frame();
1951 // if (!mouse_frame (m, ignored)) {
1952 // /* mouse is not in a canvas, try playhead+selected marker.
1953 // this is probably most true when using menus.
1956 // if (selection->markers.empty()) {
1960 // start = selection->markers.front()->position();
1961 // end = _session->audible_frame();
1965 // switch (_edit_point) {
1966 // case EditAtPlayhead:
1967 // if (selection->markers.empty()) {
1968 // /* use mouse + playhead */
1970 // end = _session->audible_frame();
1972 // /* use playhead + selected marker */
1973 // start = _session->audible_frame();
1974 // end = selection->markers.front()->position();
1978 // case EditAtMouse:
1979 // /* use mouse + selected marker */
1980 // if (selection->markers.empty()) {
1982 // end = _session->audible_frame();
1984 // start = selection->markers.front()->position();
1989 // case EditAtSelectedMarker:
1990 // /* use mouse + selected marker */
1991 // if (selection->markers.empty()) {
1993 // MessageDialog win (_("No edit range defined"),
1998 // win.set_secondary_text (
1999 // _("the edit point is Selected Marker\nbut there is no selected marker."));
2002 // win.set_default_response (RESPONSE_CLOSE);
2003 // win.set_position (Gtk::WIN_POS_MOUSE);
2008 // return false; // NO RANGE
2010 // start = selection->markers.front()->position();
2016 // if (start == end) {
2020 // if (start > end) {
2021 // swap (start, end);
2024 /* turn range into one delimited by start...end,
2034 Editor::deselect_all ()
2036 begin_reversible_selection_op (X_("Deselect All"));
2037 selection->clear ();
2038 commit_reversible_selection_op ();
2042 Editor::select_range (framepos_t s, framepos_t e)
2044 begin_reversible_selection_op (X_("Select Range"));
2045 selection->add (clicked_axisview);
2046 selection->time.clear ();
2047 long ret = selection->set (s, e);
2048 commit_reversible_selection_op ();