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();
196 case Selection::Toggle:
197 if (selection->selected (clicked_axisview)) {
198 if (group && group->is_active()) {
199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
200 if ((*i)->route_group() == group) {
201 selection->remove(*i);
205 selection->remove (clicked_axisview);
208 if (group && group->is_active()) {
209 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
210 if ((*i)->route_group() == group) {
215 selection->add (clicked_axisview);
221 if (group && group->is_active()) {
222 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
223 if ((*i)->route_group() == group) {
228 selection->add (clicked_axisview);
234 if (group && group->is_active()) {
235 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 if ((*i)->route_group() == group) {
241 selection->set (clicked_axisview);
245 case Selection::Extend:
252 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
254 begin_reversible_selection_op (X_("Set Selected Track"));
257 case Selection::Toggle:
258 if (selection->selected (&view)) {
260 selection->remove (&view);
263 selection->add (&view);
268 if (!selection->selected (&view)) {
269 selection->add (&view);
274 selection->set (&view);
277 case Selection::Extend:
278 extend_selection_to_track (view);
282 commit_reversible_selection_op ();
286 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
288 if (!clicked_routeview) {
296 set_selected_track (*clicked_routeview, op, no_remove);
300 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
302 if (!clicked_control_point) {
310 if (!selection->selected (clicked_control_point)) {
311 selection->set (clicked_control_point);
314 /* clicked on an already selected point */
318 if (selection->points.size() > 1) {
319 selection->set (clicked_control_point);
328 selection->add (clicked_control_point);
332 case Selection::Toggle:
334 /* This is a bit of a hack; if we Primary-Click-Drag a control
335 point (for push drag) we want the point we clicked on to be
336 selected, otherwise we end up confusingly dragging an
337 unselected point. So here we ensure that the point is selected
338 after the press, and if we subsequently get a release (meaning no
339 drag occurred) we set things up so that the toggle has happened.
341 if (press && !selection->selected (clicked_control_point)) {
342 /* This is the button press, and the control point is not selected; make it so,
343 in case this press leads to a drag. Also note that having done this, we don't
344 need to toggle again on release.
346 selection->toggle (clicked_control_point);
347 _control_point_toggled_on_press = true;
349 } else if (!press && !_control_point_toggled_on_press) {
350 /* This is the release, and the point wasn't toggled on the press, so do it now */
351 selection->toggle (clicked_control_point);
355 _control_point_toggled_on_press = false;
358 case Selection::Extend:
367 Editor::get_onscreen_tracks (TrackViewList& tvl)
369 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
370 if ((*i)->y_position() < _visible_canvas_height) {
376 /** Call a slot for a given `basis' track and also for any track that is in the same
377 * active route group with a particular set of properties.
379 * @param sl Slot to call.
380 * @param basis Basis track.
381 * @param prop Properties that active edit groups must share to be included in the map.
385 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
387 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
389 if (route_basis == 0) {
393 set<RouteTimeAxisView*> tracks;
394 tracks.insert (route_basis);
396 RouteGroup* group = route_basis->route()->route_group();
398 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
400 /* the basis is a member of an active route group, with the appropriate
401 properties; find other members */
403 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
404 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
405 if (v && v->route()->route_group() == group) {
412 uint32_t const sz = tracks.size ();
414 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
419 /** Call a slot for a given `basis' track and also for any track that is in the same
420 * active route group with a particular set of properties.
422 * @param sl Slot to call.
423 * @param basis Basis track.
424 * @param prop Properties that active edit groups must share to be included in the map.
428 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
430 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
431 set<boost::shared_ptr<Playlist> > playlists;
433 if (route_basis == 0) {
437 set<RouteTimeAxisView*> tracks;
438 tracks.insert (route_basis);
440 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
442 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
444 /* the basis is a member of an active route group, with the appropriate
445 properties; find other members */
447 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
448 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
450 if (v && v->route()->route_group() == group) {
452 boost::shared_ptr<Track> t = v->track();
454 if (playlists.insert (t->playlist()).second) {
455 /* haven't seen this playlist yet */
459 /* not actually a "Track", but a timeaxis view that
460 we should mapover anyway.
469 uint32_t const sz = tracks.size ();
471 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
477 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
479 boost::shared_ptr<Playlist> pl;
480 vector<boost::shared_ptr<Region> > results;
482 boost::shared_ptr<Track> tr;
484 if ((tr = tv.track()) == 0) {
489 if (&tv == &basis->get_time_axis_view()) {
490 /* looking in same track as the original */
494 if ((pl = tr->playlist()) != 0) {
495 pl->get_equivalent_regions (basis->region(), results);
498 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
499 if ((marv = tv.view()->find_view (*ir)) != 0) {
500 all_equivs->push_back (marv);
506 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
508 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);
510 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
512 equivalent_regions.push_back (basis);
516 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
518 RegionSelection equivalent;
520 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
522 vector<RegionView*> eq;
524 mapover_tracks_with_unique_playlists (
525 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
526 &(*i)->get_time_axis_view(), prop);
528 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
539 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
541 int region_count = 0;
543 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
545 RouteTimeAxisView* tatv;
547 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
549 boost::shared_ptr<Playlist> pl;
550 vector<boost::shared_ptr<Region> > results;
552 boost::shared_ptr<Track> tr;
554 if ((tr = tatv->track()) == 0) {
559 if ((pl = (tr->playlist())) != 0) {
560 pl->get_region_list_equivalent_regions (region, results);
563 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
564 if ((marv = tatv->view()->find_view (*ir)) != 0) {
577 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
579 vector<RegionView*> all_equivalent_regions;
582 if (!clicked_regionview || !clicked_routeview) {
587 button_release_can_deselect = false;
590 if (op == Selection::Toggle || op == Selection::Set) {
593 case Selection::Toggle:
594 if (selection->selected (clicked_regionview)) {
597 /* whatever was clicked was selected already; do nothing here but allow
598 the button release to deselect it
601 button_release_can_deselect = true;
604 if (button_release_can_deselect) {
606 /* just remove this one region, but only on a permitted button release */
608 selection->remove (clicked_regionview);
611 /* no more deselect action on button release till a new press
612 finds an already selected object.
615 button_release_can_deselect = false;
623 if (selection->selected (clicked_routeview)) {
624 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
626 all_equivalent_regions.push_back (clicked_regionview);
629 /* add all the equivalent regions, but only on button press */
631 if (!all_equivalent_regions.empty()) {
635 selection->add (all_equivalent_regions);
641 if (!selection->selected (clicked_regionview)) {
642 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
643 selection->set (all_equivalent_regions);
646 /* clicked on an already selected region */
650 if (selection->regions.size() > 1) {
651 /* collapse region selection down to just this one region (and its equivalents) */
652 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::group_select.property_id);
653 selection->set(all_equivalent_regions);
665 } else if (op == Selection::Extend) {
667 list<Selectable*> results;
668 framepos_t last_frame;
669 framepos_t first_frame;
670 bool same_track = false;
672 /* 1. find the last selected regionview in the track that was clicked in */
675 first_frame = max_framepos;
677 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
678 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
680 if ((*x)->region()->last_frame() > last_frame) {
681 last_frame = (*x)->region()->last_frame();
684 if ((*x)->region()->first_frame() < first_frame) {
685 first_frame = (*x)->region()->first_frame();
694 /* 2. figure out the boundaries for our search for new objects */
696 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
697 case Evoral::OverlapNone:
698 if (last_frame < clicked_regionview->region()->first_frame()) {
699 first_frame = last_frame;
700 last_frame = clicked_regionview->region()->last_frame();
702 last_frame = first_frame;
703 first_frame = clicked_regionview->region()->first_frame();
707 case Evoral::OverlapExternal:
708 if (last_frame < clicked_regionview->region()->first_frame()) {
709 first_frame = last_frame;
710 last_frame = clicked_regionview->region()->last_frame();
712 last_frame = first_frame;
713 first_frame = clicked_regionview->region()->first_frame();
717 case Evoral::OverlapInternal:
718 if (last_frame < clicked_regionview->region()->first_frame()) {
719 first_frame = last_frame;
720 last_frame = clicked_regionview->region()->last_frame();
722 last_frame = first_frame;
723 first_frame = clicked_regionview->region()->first_frame();
727 case Evoral::OverlapStart:
728 case Evoral::OverlapEnd:
729 /* nothing to do except add clicked region to selection, since it
730 overlaps with the existing selection in this track.
737 /* click in a track that has no regions selected, so extend vertically
738 to pick out all regions that are defined by the existing selection
743 first_frame = clicked_regionview->region()->position();
744 last_frame = clicked_regionview->region()->last_frame();
746 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
747 if ((*i)->region()->position() < first_frame) {
748 first_frame = (*i)->region()->position();
750 if ((*i)->region()->last_frame() + 1 > last_frame) {
751 last_frame = (*i)->region()->last_frame();
756 /* 2. find all the tracks we should select in */
758 set<RouteTimeAxisView*> relevant_tracks;
760 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
761 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
763 relevant_tracks.insert (r);
767 set<RouteTimeAxisView*> already_in_selection;
769 if (relevant_tracks.empty()) {
771 /* no tracks selected .. thus .. if the
772 regionview we're in isn't selected
773 (i.e. we're about to extend to it), then
774 find all tracks between the this one and
778 if (!selection->selected (clicked_regionview)) {
780 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
784 /* add this track to the ones we will search */
786 relevant_tracks.insert (rtv);
788 /* find the track closest to this one that
789 already a selected region.
792 RouteTimeAxisView* closest = 0;
793 int distance = INT_MAX;
794 int key = rtv->route()->presentation_info().order ();
796 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
798 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
800 if (artv && artv != rtv) {
802 pair<set<RouteTimeAxisView*>::iterator,bool> result;
804 result = already_in_selection.insert (artv);
807 /* newly added to already_in_selection */
809 int d = artv->route()->presentation_info().order ();
813 if (abs (d) < distance) {
823 /* now add all tracks between that one and this one */
825 int okey = closest->route()->presentation_info().order ();
831 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
832 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
833 if (artv && artv != rtv) {
835 int k = artv->route()->presentation_info().order ();
837 if (k >= okey && k <= key) {
839 /* in range but don't add it if
840 it already has tracks selected.
841 this avoids odd selection
842 behaviour that feels wrong.
845 if (find (already_in_selection.begin(),
846 already_in_selection.end(),
847 artv) == already_in_selection.end()) {
849 relevant_tracks.insert (artv);
859 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
860 one that was clicked.
863 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
864 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
867 /* 4. convert to a vector of regions */
869 vector<RegionView*> regions;
871 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
874 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
875 regions.push_back (arv);
879 if (!regions.empty()) {
880 selection->add (regions);
891 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
893 vector<RegionView*> all_equivalent_regions;
895 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
897 if (all_equivalent_regions.empty()) {
901 begin_reversible_selection_op (X_("set selected regions"));
904 case Selection::Toggle:
905 /* XXX this is not correct */
906 selection->toggle (all_equivalent_regions);
909 selection->set (all_equivalent_regions);
911 case Selection::Extend:
912 selection->add (all_equivalent_regions);
915 selection->add (all_equivalent_regions);
919 commit_reversible_selection_op () ;
923 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
926 boost::shared_ptr<Region> r (weak_r.lock());
932 if ((rv = sv->find_view (r)) == 0) {
936 /* don't reset the selection if its something other than
937 a single other region.
940 if (selection->regions.size() > 1) {
944 begin_reversible_selection_op (X_("set selected regions"));
948 commit_reversible_selection_op () ;
954 Editor::track_selection_changed ()
956 switch (selection->tracks.size()) {
960 set_selected_mixer_strip (*(selection->tracks.front()));
964 RouteNotificationListPtr routes (new RouteNotificationList);
965 StripableNotificationListPtr stripables (new StripableNotificationList);
967 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
969 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
971 (*i)->set_selected (yn);
973 TimeAxisView::Children c = (*i)->get_child_list ();
974 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
975 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
979 (*i)->reshow_selection (selection->time);
981 (*i)->hide_selection ();
986 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
988 routes->push_back (rtav->route());
989 stripables->push_back (rtav->route());
994 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
996 /* notify control protocols */
998 ControlProtocol::StripableSelectionChanged (stripables);
1000 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1001 uint32_t audio_track_cnt = 0;
1002 uint32_t midi_track_cnt = 0;
1004 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1005 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1008 if (atv->is_audio_track()) {
1013 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1016 if (mtv->is_midi_track()) {
1023 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1028 Editor::time_selection_changed ()
1030 /* XXX this is superficially inefficient. Hide the selection in all
1031 * tracks, then show it in all selected tracks.
1033 * However, if you investigate what this actually does, it isn't
1034 * anywhere nearly as bad as it may appear. Remember: nothing is
1035 * redrawn or even recomputed during these two loops - that only
1036 * happens when we next render ...
1039 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1040 (*i)->hide_selection ();
1043 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1044 (*i)->show_selection (selection->time);
1047 if (selection->time.empty()) {
1048 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1050 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1053 /* propagate into backend, but only when there is no drag or we are at
1054 * the end of a drag, otherwise this is too expensive (could case a
1055 * locate per mouse motion event.
1058 if (_session && !_drags->active()) {
1059 if (selection->time.length() != 0) {
1060 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1062 _session->clear_range_selection ();
1067 /** Set all region actions to have a given sensitivity */
1069 Editor::sensitize_all_region_actions (bool s)
1071 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1073 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1074 (*i)->set_sensitive (s);
1077 _all_region_actions_sensitized = s;
1080 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1081 * This method should be called just before displaying a Region menu. When a Region menu is not
1082 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1083 * on entered_regionviews work without having to check sensitivity every time the selection or
1084 * entered_regionview changes.
1086 * This method also sets up toggle action state as appropriate.
1089 Editor::sensitize_the_right_region_actions ()
1091 RegionSelection rs = get_regions_from_selection_and_entered ();
1092 sensitize_all_region_actions (!rs.empty ());
1094 _ignore_region_action = true;
1096 /* Look through the regions that are selected and make notes about what we have got */
1098 bool have_audio = false;
1099 bool have_multichannel_audio = false;
1100 bool have_midi = false;
1101 bool have_locked = false;
1102 bool have_unlocked = false;
1103 bool have_video_locked = false;
1104 bool have_video_unlocked = false;
1105 bool have_position_lock_style_audio = false;
1106 bool have_position_lock_style_music = false;
1107 bool have_muted = false;
1108 bool have_unmuted = false;
1109 bool have_opaque = false;
1110 bool have_non_opaque = false;
1111 bool have_not_at_natural_position = false;
1112 bool have_envelope_active = false;
1113 bool have_envelope_inactive = false;
1114 bool have_non_unity_scale_amplitude = false;
1115 bool have_compound_regions = false;
1116 bool have_inactive_fade_in = false;
1117 bool have_inactive_fade_out = false;
1118 bool have_active_fade_in = false;
1119 bool have_active_fade_out = false;
1120 bool have_transients = false;
1122 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1124 boost::shared_ptr<Region> r = (*i)->region ();
1125 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1129 if (ar->n_channels() > 1) {
1130 have_multichannel_audio = true;
1134 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1138 if (r->is_compound()) {
1139 have_compound_regions = true;
1145 have_unlocked = true;
1148 if (r->video_locked()) {
1149 have_video_locked = true;
1151 have_video_unlocked = true;
1154 if (r->position_lock_style() == MusicTime) {
1155 have_position_lock_style_music = true;
1157 have_position_lock_style_audio = true;
1163 have_unmuted = true;
1169 have_non_opaque = true;
1172 if (!r->at_natural_position()) {
1173 have_not_at_natural_position = true;
1176 if (r->has_transients ()){
1177 have_transients = true;
1181 if (ar->envelope_active()) {
1182 have_envelope_active = true;
1184 have_envelope_inactive = true;
1187 if (ar->scale_amplitude() != 1) {
1188 have_non_unity_scale_amplitude = true;
1191 if (ar->fade_in_active ()) {
1192 have_active_fade_in = true;
1194 have_inactive_fade_in = true;
1197 if (ar->fade_out_active ()) {
1198 have_active_fade_out = true;
1200 have_inactive_fade_out = true;
1205 _region_actions->get_action("split-region-at-transients")->set_sensitive (have_transients);
1207 if (rs.size() > 1) {
1208 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1209 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1210 _region_actions->get_action("rename-region")->set_sensitive (false);
1212 /* XXX need to check whether there is than 1 per
1213 playlist, because otherwise this makes no sense.
1215 _region_actions->get_action("combine-regions")->set_sensitive (true);
1217 _region_actions->get_action("combine-regions")->set_sensitive (false);
1219 } else if (rs.size() == 1) {
1220 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1221 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1222 _region_actions->get_action("combine-regions")->set_sensitive (false);
1225 if (!have_multichannel_audio) {
1226 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1230 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1231 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1232 _region_actions->get_action("quantize-region")->set_sensitive (false);
1233 _region_actions->get_action("legatize-region")->set_sensitive (false);
1234 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1235 _region_actions->get_action("transform-region")->set_sensitive (false);
1236 _region_actions->get_action("fork-region")->set_sensitive (false);
1237 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1238 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1239 _region_actions->get_action("transpose-region")->set_sensitive (false);
1241 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1242 /* others were already marked sensitive */
1245 if (_edit_point == EditAtMouse) {
1246 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1247 _region_actions->get_action("trim-front")->set_sensitive (false);
1248 _region_actions->get_action("trim-back")->set_sensitive (false);
1249 _region_actions->get_action("split-region")->set_sensitive (false);
1250 _region_actions->get_action("place-transient")->set_sensitive (false);
1253 if (have_compound_regions) {
1254 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1256 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1261 if (have_envelope_active && !have_envelope_inactive) {
1262 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1263 } else if (have_envelope_active && have_envelope_inactive) {
1264 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1269 _region_actions->get_action("loudness-analyze-region")->set_sensitive (false);
1270 _region_actions->get_action("spectral-analyze-region")->set_sensitive (false);
1271 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1272 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1273 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1274 _region_actions->get_action("strip-region-silence")->set_sensitive (false);
1275 _region_actions->get_action("show-rhythm-ferret")->set_sensitive (false);
1279 if (!have_non_unity_scale_amplitude || !have_audio) {
1280 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1283 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1284 a->set_active (have_locked && !have_unlocked);
1285 if (have_locked && have_unlocked) {
1286 // a->set_inconsistent ();
1289 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1290 a->set_active (have_video_locked && !have_video_unlocked);
1291 if (have_video_locked && have_video_unlocked) {
1292 // a->set_inconsistent ();
1295 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1296 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1298 if (have_position_lock_style_music && have_position_lock_style_audio) {
1299 // a->set_inconsistent ();
1302 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1303 a->set_active (have_muted && !have_unmuted);
1304 if (have_muted && have_unmuted) {
1305 // a->set_inconsistent ();
1308 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1309 a->set_active (have_opaque && !have_non_opaque);
1310 if (have_opaque && have_non_opaque) {
1311 // a->set_inconsistent ();
1314 if (!have_not_at_natural_position) {
1315 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1318 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1319 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1320 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1322 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1325 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1326 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1327 if (have_active_fade_in && have_inactive_fade_in) {
1328 // a->set_inconsistent ();
1331 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1332 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1334 if (have_active_fade_out && have_inactive_fade_out) {
1335 // a->set_inconsistent ();
1338 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1339 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1341 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1342 a->set_active (have_active_fade && !have_inactive_fade);
1344 if (have_active_fade && have_inactive_fade) {
1345 // a->set_inconsistent ();
1348 _ignore_region_action = false;
1350 _all_region_actions_sensitized = false;
1355 Editor::region_selection_changed ()
1357 _regions->block_change_connection (true);
1358 editor_regions_selection_changed_connection.block(true);
1360 if (_region_selection_change_updates_region_list) {
1361 _regions->unselect_all ();
1364 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1365 (*i)->set_selected_regionviews (selection->regions);
1368 if (_region_selection_change_updates_region_list) {
1369 _regions->set_selected (selection->regions);
1372 _regions->block_change_connection (false);
1373 editor_regions_selection_changed_connection.block(false);
1375 if (selection->regions.empty()) {
1376 sensitize_all_region_actions (false);
1378 if (!_all_region_actions_sensitized) {
1379 /* This selection change might have changed what region actions
1380 are allowed, so sensitize them all in case a key is pressed.
1382 sensitize_all_region_actions (true);
1386 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1387 maybe_locate_with_edit_preroll (selection->regions.start());
1390 /* propagate into backend */
1393 if (!selection->regions.empty()) {
1394 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1396 _session->clear_object_selection ();
1403 Editor::point_selection_changed ()
1405 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1406 (*i)->set_selected_points (selection->points);
1411 Editor::select_all_in_track (Selection::Operation op)
1413 list<Selectable *> touched;
1415 if (!clicked_routeview) {
1419 begin_reversible_selection_op (X_("Select All in Track"));
1421 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1424 case Selection::Toggle:
1425 selection->add (touched);
1427 case Selection::Set:
1428 selection->set (touched);
1430 case Selection::Extend:
1431 /* meaningless, because we're selecting everything */
1433 case Selection::Add:
1434 selection->add (touched);
1438 commit_reversible_selection_op ();
1442 Editor::select_all_internal_edit (Selection::Operation)
1444 bool selected = false;
1446 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1447 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1449 mrv->select_all_notes ();
1454 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1456 mrv->select_all_notes ();
1464 Editor::select_all_objects (Selection::Operation op)
1466 list<Selectable *> touched;
1468 TrackViewList ts = track_views;
1470 if (internal_editing() && select_all_internal_edit(op)) {
1471 return; // Selected notes
1474 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1475 if ((*iter)->hidden()) {
1478 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1479 selection->add (*iter);
1483 begin_reversible_selection_op (X_("select all"));
1485 case Selection::Add:
1486 selection->add (touched);
1488 case Selection::Toggle:
1489 selection->add (touched);
1491 case Selection::Set:
1492 selection->set (touched);
1494 case Selection::Extend:
1495 /* meaningless, because we're selecting everything */
1498 commit_reversible_selection_op ();
1502 Editor::invert_selection_in_track ()
1504 list<Selectable *> touched;
1506 if (!clicked_routeview) {
1510 begin_reversible_selection_op (X_("Invert Selection in Track"));
1511 clicked_routeview->get_inverted_selectables (*selection, touched);
1512 selection->set (touched);
1513 commit_reversible_selection_op ();
1517 Editor::invert_selection ()
1519 list<Selectable *> touched;
1521 if (internal_editing()) {
1522 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1523 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1525 mrv->invert_selection ();
1531 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1532 if ((*iter)->hidden()) {
1535 (*iter)->get_inverted_selectables (*selection, touched);
1538 begin_reversible_selection_op (X_("Invert Selection"));
1539 selection->set (touched);
1540 commit_reversible_selection_op ();
1543 /** @param start Start time in session frames.
1544 * @param end End time in session frames.
1545 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1546 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1547 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1548 * within the region are already selected.
1551 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1553 list<Selectable*> found;
1555 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1557 if ((*iter)->hidden()) {
1561 (*iter)->get_selectables (start, end, top, bot, found);
1564 if (found.empty()) {
1565 selection->clear_objects();
1566 selection->clear_time ();
1570 if (preserve_if_selected && op != Selection::Toggle) {
1571 list<Selectable*>::iterator i = found.begin();
1572 while (i != found.end() && (*i)->selected()) {
1576 if (i == found.end()) {
1581 begin_reversible_selection_op (X_("select all within"));
1583 case Selection::Add:
1584 selection->add (found);
1586 case Selection::Toggle:
1587 selection->toggle (found);
1589 case Selection::Set:
1590 selection->set (found);
1592 case Selection::Extend:
1593 /* not defined yet */
1597 commit_reversible_selection_op ();
1601 Editor::set_selection_from_region ()
1603 if (selection->regions.empty()) {
1607 /* find all the tracks that have selected regions */
1609 set<TimeAxisView*> tracks;
1611 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1612 tracks.insert (&(*r)->get_time_axis_view());
1616 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1618 /* select range (this will clear the region selection) */
1620 selection->set (selection->regions.start(), selection->regions.end_frame());
1622 /* and select the tracks */
1624 selection->set (tvl);
1626 set_mouse_mode (Editing::MouseRange, false);
1630 Editor::set_selection_from_punch()
1634 if ((location = _session->locations()->auto_punch_location()) == 0) {
1638 set_selection_from_range (*location);
1642 Editor::set_selection_from_loop()
1646 if ((location = _session->locations()->auto_loop_location()) == 0) {
1649 set_selection_from_range (*location);
1653 Editor::set_selection_from_range (Location& loc)
1655 begin_reversible_selection_op (X_("set selection from range"));
1656 selection->set (loc.start(), loc.end());
1657 commit_reversible_selection_op ();
1659 set_mouse_mode (Editing::MouseRange, false);
1663 Editor::select_all_selectables_using_time_selection ()
1665 list<Selectable *> touched;
1667 if (selection->time.empty()) {
1671 framepos_t start = selection->time[clicked_selection].start;
1672 framepos_t end = selection->time[clicked_selection].end;
1674 if (end - start < 1) {
1680 if (selection->tracks.empty()) {
1683 ts = &selection->tracks;
1686 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1687 if ((*iter)->hidden()) {
1690 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1693 begin_reversible_selection_op (X_("select all from range"));
1694 selection->set (touched);
1695 commit_reversible_selection_op ();
1700 Editor::select_all_selectables_using_punch()
1702 Location* location = _session->locations()->auto_punch_location();
1703 list<Selectable *> touched;
1705 if (location == 0 || (location->end() - location->start() <= 1)) {
1712 if (selection->tracks.empty()) {
1715 ts = &selection->tracks;
1718 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1719 if ((*iter)->hidden()) {
1722 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1724 begin_reversible_selection_op (X_("select all from punch"));
1725 selection->set (touched);
1726 commit_reversible_selection_op ();
1731 Editor::select_all_selectables_using_loop()
1733 Location* location = _session->locations()->auto_loop_location();
1734 list<Selectable *> touched;
1736 if (location == 0 || (location->end() - location->start() <= 1)) {
1743 if (selection->tracks.empty()) {
1746 ts = &selection->tracks;
1749 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1750 if ((*iter)->hidden()) {
1753 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1755 begin_reversible_selection_op (X_("select all from loop"));
1756 selection->set (touched);
1757 commit_reversible_selection_op ();
1762 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1766 list<Selectable *> touched;
1769 start = cursor->current_frame();
1770 end = _session->current_end_frame();
1772 if (cursor->current_frame() > 0) {
1774 end = cursor->current_frame() - 1;
1780 if (internal_editing()) {
1781 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1782 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1784 mrv->select_range (start, end);
1791 begin_reversible_selection_op (X_("select all after cursor"));
1793 begin_reversible_selection_op (X_("select all before cursor"));
1798 if (selection->tracks.empty()) {
1801 ts = &selection->tracks;
1804 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1805 if ((*iter)->hidden()) {
1808 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1810 selection->set (touched);
1811 commit_reversible_selection_op ();
1815 Editor::select_all_selectables_using_edit (bool after, bool from_context_menu)
1819 list<Selectable *> touched;
1822 start = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu);
1823 end = _session->current_end_frame();
1825 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, from_context_menu)) > 1) {
1833 if (internal_editing()) {
1834 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1835 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1836 mrv->select_range (start, end);
1842 begin_reversible_selection_op (X_("select all after edit"));
1844 begin_reversible_selection_op (X_("select all before edit"));
1849 if (selection->tracks.empty()) {
1852 ts = &selection->tracks;
1855 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1856 if ((*iter)->hidden()) {
1859 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1861 selection->set (touched);
1862 commit_reversible_selection_op ();
1866 Editor::select_all_selectables_between (bool within)
1870 list<Selectable *> touched;
1872 if (!get_edit_op_range (start, end)) {
1876 if (internal_editing()) {
1877 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1878 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1879 mrv->select_range (start, end);
1886 if (selection->tracks.empty()) {
1889 ts = &selection->tracks;
1892 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1893 if ((*iter)->hidden()) {
1896 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1899 begin_reversible_selection_op (X_("Select all Selectables Between"));
1900 selection->set (touched);
1901 commit_reversible_selection_op ();
1905 Editor::select_range_between ()
1910 if ( !selection->time.empty() ) {
1911 selection->clear_time ();
1914 if (!get_edit_op_range (start, end)) {
1918 begin_reversible_selection_op (X_("Select Range Between"));
1919 set_mouse_mode (MouseRange);
1920 selection->set (start, end);
1921 commit_reversible_selection_op ();
1925 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1930 /* if an explicit range exists, use it */
1932 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1933 /* we know that these are ordered */
1934 start = selection->time.start();
1935 end = selection->time.end_frame();
1943 // if (!mouse_frame (m, ignored)) {
1944 // /* mouse is not in a canvas, try playhead+selected marker.
1945 // this is probably most true when using menus.
1948 // if (selection->markers.empty()) {
1952 // start = selection->markers.front()->position();
1953 // end = _session->audible_frame();
1957 // switch (_edit_point) {
1958 // case EditAtPlayhead:
1959 // if (selection->markers.empty()) {
1960 // /* use mouse + playhead */
1962 // end = _session->audible_frame();
1964 // /* use playhead + selected marker */
1965 // start = _session->audible_frame();
1966 // end = selection->markers.front()->position();
1970 // case EditAtMouse:
1971 // /* use mouse + selected marker */
1972 // if (selection->markers.empty()) {
1974 // end = _session->audible_frame();
1976 // start = selection->markers.front()->position();
1981 // case EditAtSelectedMarker:
1982 // /* use mouse + selected marker */
1983 // if (selection->markers.empty()) {
1985 // MessageDialog win (_("No edit range defined"),
1990 // win.set_secondary_text (
1991 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1994 // win.set_default_response (RESPONSE_CLOSE);
1995 // win.set_position (Gtk::WIN_POS_MOUSE);
2000 // return false; // NO RANGE
2002 // start = selection->markers.front()->position();
2008 // if (start == end) {
2012 // if (start > end) {
2013 // swap (start, end);
2016 /* turn range into one delimited by start...end,
2026 Editor::deselect_all ()
2028 begin_reversible_selection_op (X_("Deselect All"));
2029 selection->clear ();
2030 commit_reversible_selection_op ();
2034 Editor::select_range (framepos_t s, framepos_t e)
2036 begin_reversible_selection_op (X_("Select Range"));
2037 selection->add (clicked_axisview);
2038 selection->time.clear ();
2039 long ret = selection->set (s, e);
2040 commit_reversible_selection_op ();