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"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "audio_streamview.h"
38 #include "automation_line.h"
39 #include "control_point.h"
40 #include "editor_regions.h"
41 #include "editor_cursors.h"
42 #include "midi_region_view.h"
47 using namespace ARDOUR;
51 using namespace Gtkmm2ext;
52 using namespace Editing;
54 struct TrackViewByPositionSorter
56 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
57 return a->y_position() < b->y_position();
62 Editor::extend_selection_to_track (TimeAxisView& view)
64 if (selection->selected (&view)) {
65 /* already selected, do nothing */
69 if (selection->tracks.empty()) {
71 if (!selection->selected (&view)) {
72 selection->set (&view);
79 /* something is already selected, so figure out which range of things to add */
81 TrackViewList to_be_added;
82 TrackViewList sorted = track_views;
83 TrackViewByPositionSorter cmp;
84 bool passed_clicked = false;
89 if (!selection->selected (&view)) {
90 to_be_added.push_back (&view);
93 /* figure out if we should go forward or backwards */
95 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
98 passed_clicked = true;
101 if (selection->selected (*i)) {
102 if (passed_clicked) {
111 passed_clicked = false;
115 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
118 passed_clicked = true;
122 if (passed_clicked) {
123 if ((*i)->hidden()) {
126 if (selection->selected (*i)) {
128 } else if (!(*i)->hidden()) {
129 to_be_added.push_back (*i);
136 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
139 passed_clicked = true;
143 if (passed_clicked) {
145 if ((*r)->hidden()) {
149 if (selection->selected (*r)) {
151 } else if (!(*r)->hidden()) {
152 to_be_added.push_back (*r);
158 if (!to_be_added.empty()) {
159 selection->add (to_be_added);
167 Editor::select_all_tracks ()
169 TrackViewList visible_views;
170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
171 if ((*i)->marked_for_display()) {
172 visible_views.push_back (*i);
175 selection->set (visible_views);
178 /** Select clicked_axisview, unless there are no currently selected
179 * tracks, in which case nothing will happen unless `force' is true.
182 Editor::set_selected_track_as_side_effect (Selection::Operation op)
184 if (!clicked_axisview) {
188 RouteGroup* group = NULL;
189 if (clicked_routeview) {
190 group = clicked_routeview->route()->route_group();
193 bool had_tracks = !selection->tracks.empty();
194 RouteGroup& arg (_session->all_route_group());
197 case Selection::Toggle:
198 if (selection->selected (clicked_axisview)) {
199 if (arg.is_select() && arg.is_active()) {
200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
201 selection->remove(*i);
203 } else if (group && group->is_active()) {
204 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
205 if ((*i)->route_group() == group)
206 selection->remove(*i);
209 selection->remove (clicked_axisview);
212 if (arg.is_select() && arg.is_active()) {
213 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216 } else if (group && group->is_active()) {
217 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
218 if ( (*i)->route_group() == group)
222 selection->add (clicked_axisview);
228 if (!had_tracks && arg.is_select() && arg.is_active()) {
229 /* nothing was selected already, and all group is active etc. so use
232 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
235 } else if (group && group->is_active()) {
236 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
237 if ((*i)->route_group() == group)
241 selection->add (clicked_axisview);
247 if (!had_tracks && arg.is_select() && arg.is_active()) {
248 /* nothing was selected already, and all group is active etc. so use
251 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
254 } else if (group && group->is_active()) {
255 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
256 if ((*i)->route_group() == group)
260 selection->set (clicked_axisview);
264 case Selection::Extend:
271 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
274 case Selection::Toggle:
275 if (selection->selected (&view)) {
277 selection->remove (&view);
280 selection->add (&view);
285 if (!selection->selected (&view)) {
286 selection->add (&view);
291 selection->set (&view);
294 case Selection::Extend:
295 extend_selection_to_track (view);
301 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
303 if (!clicked_routeview) {
311 set_selected_track (*clicked_routeview, op, no_remove);
315 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
317 if (!clicked_control_point) {
324 selection->set (clicked_control_point);
329 selection->add (clicked_control_point);
332 case Selection::Toggle:
333 /* This is a bit of a hack; if we Primary-Click-Drag a control
334 point (for push drag) we want the point we clicked on to be
335 selected, otherwise we end up confusingly dragging an
336 unselected point. So here we ensure that the point is selected
337 after the press, and if we subsequently get a release (meaning no
338 drag occurred) we set things up so that the toggle has happened.
340 if (press && !selection->selected (clicked_control_point)) {
341 /* This is the button press, and the control point is not selected; make it so,
342 in case this press leads to a drag. Also note that having done this, we don't
343 need to toggle again on release.
345 selection->toggle (clicked_control_point);
346 _control_point_toggled_on_press = true;
347 } else if (!press && !_control_point_toggled_on_press) {
348 /* This is the release, and the point wasn't toggled on the press, so do it now */
349 selection->toggle (clicked_control_point);
352 _control_point_toggled_on_press = false;
355 case Selection::Extend:
364 Editor::get_onscreen_tracks (TrackViewList& tvl)
366 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
367 if ((*i)->y_position() < _visible_canvas_height) {
373 /** Call a slot for a given `basis' track and also for any track that is in the same
374 * active route group with a particular set of properties.
376 * @param sl Slot to call.
377 * @param basis Basis track.
378 * @param prop Properties that active edit groups must share to be included in the map.
382 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
384 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
386 if (route_basis == 0) {
390 set<RouteTimeAxisView*> tracks;
391 tracks.insert (route_basis);
393 RouteGroup* group = route_basis->route()->route_group();
395 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
397 /* the basis is a member of an active route group, with the appropriate
398 properties; find other members */
400 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
401 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
402 if (v && v->route()->route_group() == group) {
409 uint32_t const sz = tracks.size ();
411 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
416 /** Call a slot for a given `basis' track and also for any track that is in the same
417 * active route group with a particular set of properties.
419 * @param sl Slot to call.
420 * @param basis Basis track.
421 * @param prop Properties that active edit groups must share to be included in the map.
425 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
427 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
428 set<boost::shared_ptr<Playlist> > playlists;
430 if (route_basis == 0) {
434 set<RouteTimeAxisView*> tracks;
435 tracks.insert (route_basis);
437 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
439 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
441 /* the basis is a member of an active route group, with the appropriate
442 properties; find other members */
444 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
445 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
447 if (v && v->route()->route_group() == group) {
449 boost::shared_ptr<Track> t = v->track();
451 if (playlists.insert (t->playlist()).second) {
452 /* haven't seen this playlist yet */
456 /* not actually a "Track", but a timeaxis view that
457 we should mapover anyway.
466 uint32_t const sz = tracks.size ();
468 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
474 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
476 boost::shared_ptr<Playlist> pl;
477 vector<boost::shared_ptr<Region> > results;
479 boost::shared_ptr<Track> tr;
481 if ((tr = tv.track()) == 0) {
486 if (&tv == &basis->get_time_axis_view()) {
487 /* looking in same track as the original */
491 if ((pl = tr->playlist()) != 0) {
492 pl->get_equivalent_regions (basis->region(), results);
495 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
496 if ((marv = tv.view()->find_view (*ir)) != 0) {
497 all_equivs->push_back (marv);
503 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
505 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);
507 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
509 equivalent_regions.push_back (basis);
513 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
515 RegionSelection equivalent;
517 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
519 vector<RegionView*> eq;
521 mapover_tracks_with_unique_playlists (
522 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
523 &(*i)->get_time_axis_view(), prop);
525 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
536 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
538 int region_count = 0;
540 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
542 RouteTimeAxisView* tatv;
544 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
546 boost::shared_ptr<Playlist> pl;
547 vector<boost::shared_ptr<Region> > results;
549 boost::shared_ptr<Track> tr;
551 if ((tr = tatv->track()) == 0) {
556 if ((pl = (tr->playlist())) != 0) {
557 pl->get_region_list_equivalent_regions (region, results);
560 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
561 if ((marv = tatv->view()->find_view (*ir)) != 0) {
574 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
576 vector<RegionView*> all_equivalent_regions;
579 if (!clicked_regionview || !clicked_routeview) {
584 button_release_can_deselect = false;
587 if (op == Selection::Toggle || op == Selection::Set) {
590 case Selection::Toggle:
591 if (selection->selected (clicked_regionview)) {
594 /* whatever was clicked was selected already; do nothing here but allow
595 the button release to deselect it
598 button_release_can_deselect = true;
601 if (button_release_can_deselect) {
603 /* just remove this one region, but only on a permitted button release */
605 selection->remove (clicked_regionview);
608 /* no more deselect action on button release till a new press
609 finds an already selected object.
612 button_release_can_deselect = false;
620 if (selection->selected (clicked_routeview)) {
621 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
623 all_equivalent_regions.push_back (clicked_regionview);
626 /* add all the equivalent regions, but only on button press */
628 if (!all_equivalent_regions.empty()) {
632 selection->add (all_equivalent_regions);
638 if (!selection->selected (clicked_regionview)) {
639 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
640 selection->set (all_equivalent_regions);
643 /* clicked on an already selected region */
647 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
648 selection->set(all_equivalent_regions);
659 } else if (op == Selection::Extend) {
661 list<Selectable*> results;
662 framepos_t last_frame;
663 framepos_t first_frame;
664 bool same_track = false;
666 /* 1. find the last selected regionview in the track that was clicked in */
669 first_frame = max_framepos;
671 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
672 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
674 if ((*x)->region()->last_frame() > last_frame) {
675 last_frame = (*x)->region()->last_frame();
678 if ((*x)->region()->first_frame() < first_frame) {
679 first_frame = (*x)->region()->first_frame();
688 /* 2. figure out the boundaries for our search for new objects */
690 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
691 case Evoral::OverlapNone:
692 if (last_frame < clicked_regionview->region()->first_frame()) {
693 first_frame = last_frame;
694 last_frame = clicked_regionview->region()->last_frame();
696 last_frame = first_frame;
697 first_frame = clicked_regionview->region()->first_frame();
701 case Evoral::OverlapExternal:
702 if (last_frame < clicked_regionview->region()->first_frame()) {
703 first_frame = last_frame;
704 last_frame = clicked_regionview->region()->last_frame();
706 last_frame = first_frame;
707 first_frame = clicked_regionview->region()->first_frame();
711 case Evoral::OverlapInternal:
712 if (last_frame < clicked_regionview->region()->first_frame()) {
713 first_frame = last_frame;
714 last_frame = clicked_regionview->region()->last_frame();
716 last_frame = first_frame;
717 first_frame = clicked_regionview->region()->first_frame();
721 case Evoral::OverlapStart:
722 case Evoral::OverlapEnd:
723 /* nothing to do except add clicked region to selection, since it
724 overlaps with the existing selection in this track.
731 /* click in a track that has no regions selected, so extend vertically
732 to pick out all regions that are defined by the existing selection
737 first_frame = clicked_regionview->region()->position();
738 last_frame = clicked_regionview->region()->last_frame();
740 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
741 if ((*i)->region()->position() < first_frame) {
742 first_frame = (*i)->region()->position();
744 if ((*i)->region()->last_frame() + 1 > last_frame) {
745 last_frame = (*i)->region()->last_frame();
750 /* 2. find all the tracks we should select in */
752 set<RouteTimeAxisView*> relevant_tracks;
754 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
755 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
757 relevant_tracks.insert (r);
761 set<RouteTimeAxisView*> already_in_selection;
763 if (relevant_tracks.empty()) {
765 /* no tracks selected .. thus .. if the
766 regionview we're in isn't selected
767 (i.e. we're about to extend to it), then
768 find all tracks between the this one and
772 if (!selection->selected (clicked_regionview)) {
774 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
778 /* add this track to the ones we will search */
780 relevant_tracks.insert (rtv);
782 /* find the track closest to this one that
783 already a selected region.
786 RouteTimeAxisView* closest = 0;
787 int distance = INT_MAX;
788 int key = rtv->route()->order_key ();
790 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
792 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
794 if (artv && artv != rtv) {
796 pair<set<RouteTimeAxisView*>::iterator,bool> result;
798 result = already_in_selection.insert (artv);
801 /* newly added to already_in_selection */
803 int d = artv->route()->order_key ();
807 if (abs (d) < distance) {
817 /* now add all tracks between that one and this one */
819 int okey = closest->route()->order_key ();
825 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
826 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
827 if (artv && artv != rtv) {
829 int k = artv->route()->order_key ();
831 if (k >= okey && k <= key) {
833 /* in range but don't add it if
834 it already has tracks selected.
835 this avoids odd selection
836 behaviour that feels wrong.
839 if (find (already_in_selection.begin(),
840 already_in_selection.end(),
841 artv) == already_in_selection.end()) {
843 relevant_tracks.insert (artv);
853 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
854 one that was clicked.
857 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
858 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
861 /* 4. convert to a vector of regions */
863 vector<RegionView*> regions;
865 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
868 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
869 regions.push_back (arv);
873 if (!regions.empty()) {
874 selection->add (regions);
885 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
887 vector<RegionView*> all_equivalent_regions;
889 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
891 if (all_equivalent_regions.empty()) {
895 begin_reversible_command (_("set selected regions"));
898 case Selection::Toggle:
899 /* XXX this is not correct */
900 selection->toggle (all_equivalent_regions);
903 selection->set (all_equivalent_regions);
905 case Selection::Extend:
906 selection->add (all_equivalent_regions);
909 selection->add (all_equivalent_regions);
913 commit_reversible_command () ;
917 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
920 boost::shared_ptr<Region> r (weak_r.lock());
926 if ((rv = sv->find_view (r)) == 0) {
930 /* don't reset the selection if its something other than
931 a single other region.
934 if (selection->regions.size() > 1) {
938 begin_reversible_command (_("set selected regions"));
942 commit_reversible_command () ;
948 Editor::track_selection_changed ()
950 switch (selection->tracks.size()) {
954 set_selected_mixer_strip (*(selection->tracks.front()));
958 RouteNotificationListPtr routes (new RouteNotificationList);
960 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
962 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
964 (*i)->set_selected (yn);
966 TimeAxisView::Children c = (*i)->get_child_list ();
967 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
968 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
972 (*i)->reshow_selection (selection->time);
974 (*i)->hide_selection ();
979 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
981 routes->push_back (rtav->route());
986 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
988 /* notify control protocols */
990 ControlProtocol::TrackSelectionChanged (routes);
994 Editor::time_selection_changed ()
996 if (Profile->get_sae()) {
1000 /* XXX this is superficially inefficient. Hide the selection in all
1001 * tracks, then show it in all selected tracks.
1003 * However, if you investigate what this actually does, it isn't
1004 * anywhere nearly as bad as it may appear. Remember: nothing is
1005 * redrawn or even recomputed during these two loops - that only
1006 * happens when we next render ...
1009 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1010 (*i)->hide_selection ();
1013 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1014 (*i)->show_selection (selection->time);
1017 if (selection->time.empty()) {
1018 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1020 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1024 /** Set all region actions to have a given sensitivity */
1026 Editor::sensitize_all_region_actions (bool s)
1028 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1030 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1031 (*i)->set_sensitive (s);
1034 _all_region_actions_sensitized = s;
1037 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1038 * This method should be called just before displaying a Region menu. When a Region menu is not
1039 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1040 * on entered_regionviews work without having to check sensitivity every time the selection or
1041 * entered_regionview changes.
1043 * This method also sets up toggle action state as appropriate.
1046 Editor::sensitize_the_right_region_actions ()
1049 RegionSelection rs = get_regions_from_selection_and_entered ();
1050 sensitize_all_region_actions (!rs.empty ());
1052 _ignore_region_action = true;
1054 /* Look through the regions that are selected and make notes about what we have got */
1056 bool have_audio = false;
1057 bool have_multichannel_audio = false;
1058 bool have_midi = false;
1059 bool have_locked = false;
1060 bool have_unlocked = false;
1061 bool have_video_locked = false;
1062 bool have_video_unlocked = false;
1063 bool have_position_lock_style_audio = false;
1064 bool have_position_lock_style_music = false;
1065 bool have_muted = false;
1066 bool have_unmuted = false;
1067 bool have_opaque = false;
1068 bool have_non_opaque = false;
1069 bool have_not_at_natural_position = false;
1070 bool have_envelope_active = false;
1071 bool have_envelope_inactive = false;
1072 bool have_non_unity_scale_amplitude = false;
1073 bool have_compound_regions = false;
1074 bool have_inactive_fade_in = false;
1075 bool have_inactive_fade_out = false;
1076 bool have_active_fade_in = false;
1077 bool have_active_fade_out = false;
1079 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1081 boost::shared_ptr<Region> r = (*i)->region ();
1082 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1086 if (ar->n_channels() > 1) {
1087 have_multichannel_audio = true;
1091 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1095 if (r->is_compound()) {
1096 have_compound_regions = true;
1102 have_unlocked = true;
1105 if (r->video_locked()) {
1106 have_video_locked = true;
1108 have_video_unlocked = true;
1111 if (r->position_lock_style() == MusicTime) {
1112 have_position_lock_style_music = true;
1114 have_position_lock_style_audio = true;
1120 have_unmuted = true;
1126 have_non_opaque = true;
1129 if (!r->at_natural_position()) {
1130 have_not_at_natural_position = true;
1134 if (ar->envelope_active()) {
1135 have_envelope_active = true;
1137 have_envelope_inactive = true;
1140 if (ar->scale_amplitude() != 1) {
1141 have_non_unity_scale_amplitude = true;
1144 if (ar->fade_in_active ()) {
1145 have_active_fade_in = true;
1147 have_inactive_fade_in = true;
1150 if (ar->fade_out_active ()) {
1151 have_active_fade_out = true;
1153 have_inactive_fade_out = true;
1158 if (rs.size() > 1) {
1159 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1160 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1161 _region_actions->get_action("rename-region")->set_sensitive (false);
1163 /* XXX need to check whether there is than 1 per
1164 playlist, because otherwise this makes no sense.
1166 _region_actions->get_action("combine-regions")->set_sensitive (true);
1168 _region_actions->get_action("combine-regions")->set_sensitive (false);
1170 } else if (rs.size() == 1) {
1171 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1172 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1173 _region_actions->get_action("combine-regions")->set_sensitive (false);
1176 if (!have_multichannel_audio) {
1177 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1181 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1182 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1183 _region_actions->get_action("quantize-region")->set_sensitive (false);
1184 _region_actions->get_action("legatize-region")->set_sensitive (false);
1185 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1186 _region_actions->get_action("fork-region")->set_sensitive (false);
1187 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1188 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1189 _region_actions->get_action("transpose-region")->set_sensitive (false);
1191 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1192 /* others were already marked sensitive */
1195 if (_edit_point == EditAtMouse) {
1196 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1197 _region_actions->get_action("trim-front")->set_sensitive (false);
1198 _region_actions->get_action("trim-back")->set_sensitive (false);
1199 _region_actions->get_action("split-region")->set_sensitive (false);
1200 _region_actions->get_action("place-transient")->set_sensitive (false);
1203 if (have_compound_regions) {
1204 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1206 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1211 if (have_envelope_active && !have_envelope_inactive) {
1212 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1213 } else if (have_envelope_active && have_envelope_inactive) {
1214 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1219 _region_actions->get_action("analyze-region")->set_sensitive (false);
1220 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1221 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1222 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1226 if (!have_non_unity_scale_amplitude || !have_audio) {
1227 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1230 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1231 a->set_active (have_locked && !have_unlocked);
1232 if (have_locked && have_unlocked) {
1233 // a->set_inconsistent ();
1236 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1237 a->set_active (have_video_locked && !have_video_unlocked);
1238 if (have_video_locked && have_video_unlocked) {
1239 // a->set_inconsistent ();
1242 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1243 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1245 if (have_position_lock_style_music && have_position_lock_style_audio) {
1246 // a->set_inconsistent ();
1249 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1250 a->set_active (have_muted && !have_unmuted);
1251 if (have_muted && have_unmuted) {
1252 // a->set_inconsistent ();
1255 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1256 a->set_active (have_opaque && !have_non_opaque);
1257 if (have_opaque && have_non_opaque) {
1258 // a->set_inconsistent ();
1261 if (!have_not_at_natural_position) {
1262 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1265 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1266 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1267 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1269 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1272 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1273 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1274 if (have_active_fade_in && have_inactive_fade_in) {
1275 // a->set_inconsistent ();
1278 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1279 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1281 if (have_active_fade_out && have_inactive_fade_out) {
1282 // a->set_inconsistent ();
1285 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1286 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1288 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1289 a->set_active (have_active_fade && !have_inactive_fade);
1291 if (have_active_fade && have_inactive_fade) {
1292 // a->set_inconsistent ();
1295 _ignore_region_action = false;
1297 _all_region_actions_sensitized = false;
1302 Editor::region_selection_changed ()
1304 _regions->block_change_connection (true);
1305 editor_regions_selection_changed_connection.block(true);
1307 if (_region_selection_change_updates_region_list) {
1308 _regions->unselect_all ();
1311 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1312 (*i)->set_selected_regionviews (selection->regions);
1315 if (_region_selection_change_updates_region_list) {
1316 _regions->set_selected (selection->regions);
1319 _regions->block_change_connection (false);
1320 editor_regions_selection_changed_connection.block(false);
1322 if (!_all_region_actions_sensitized) {
1323 /* This selection change might have changed what region actions
1324 are allowed, so sensitize them all in case a key is pressed.
1326 sensitize_all_region_actions (true);
1329 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1330 maybe_locate_with_edit_preroll (selection->regions.start());
1335 Editor::point_selection_changed ()
1337 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1338 (*i)->set_selected_points (selection->points);
1343 Editor::select_all_in_track (Selection::Operation op)
1345 list<Selectable *> touched;
1347 if (!clicked_routeview) {
1351 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1354 case Selection::Toggle:
1355 selection->add (touched);
1357 case Selection::Set:
1358 selection->set (touched);
1360 case Selection::Extend:
1361 /* meaningless, because we're selecting everything */
1363 case Selection::Add:
1364 selection->add (touched);
1370 Editor::select_all_internal_edit (Selection::Operation)
1372 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1375 mrv->select_all_notes ();
1381 Editor::select_all_objects (Selection::Operation op)
1383 list<Selectable *> touched;
1385 TrackViewList ts = track_views;
1387 if (internal_editing()) {
1389 bool midi_selected = false;
1391 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1392 if ((*iter)->hidden()) {
1396 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*iter);
1398 if (rtav && rtav->is_midi_track()) {
1399 midi_selected = true;
1404 if (midi_selected) {
1405 select_all_internal_edit (op);
1410 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1411 if ((*iter)->hidden()) {
1414 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1415 selection->add (*iter);
1419 begin_reversible_command (_("select all"));
1421 case Selection::Add:
1422 selection->add (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 */
1434 commit_reversible_command ();
1438 Editor::invert_selection_in_track ()
1440 list<Selectable *> touched;
1442 if (!clicked_routeview) {
1446 clicked_routeview->get_inverted_selectables (*selection, touched);
1447 selection->set (touched);
1451 Editor::invert_selection ()
1453 list<Selectable *> touched;
1455 if (internal_editing()) {
1456 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1457 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1459 mrv->invert_selection ();
1465 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1466 if ((*iter)->hidden()) {
1469 (*iter)->get_inverted_selectables (*selection, touched);
1472 selection->set (touched);
1475 /** @param start Start time in session frames.
1476 * @param end End time in session frames.
1477 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1478 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1479 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1480 * within the region are already selected.
1483 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1485 list<Selectable*> found;
1487 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1489 if ((*iter)->hidden()) {
1493 (*iter)->get_selectables (start, end, top, bot, found);
1496 if (found.empty()) {
1497 selection->clear_objects();
1498 selection->clear_time ();
1502 if (preserve_if_selected && op != Selection::Toggle) {
1503 list<Selectable*>::iterator i = found.begin();
1504 while (i != found.end() && (*i)->get_selected()) {
1508 if (i == found.end()) {
1513 begin_reversible_command (_("select all within"));
1515 case Selection::Add:
1516 selection->add (found);
1518 case Selection::Toggle:
1519 selection->toggle (found);
1521 case Selection::Set:
1522 selection->set (found);
1524 case Selection::Extend:
1525 /* not defined yet */
1529 commit_reversible_command ();
1533 Editor::set_selection_from_region ()
1535 if (selection->regions.empty()) {
1539 selection->set (selection->regions.start(), selection->regions.end_frame());
1540 if (!Profile->get_sae()) {
1541 set_mouse_mode (Editing::MouseRange, false);
1546 Editor::set_selection_from_punch()
1550 if ((location = _session->locations()->auto_punch_location()) == 0) {
1554 set_selection_from_range (*location);
1558 Editor::set_selection_from_loop()
1562 if ((location = _session->locations()->auto_loop_location()) == 0) {
1565 set_selection_from_range (*location);
1569 Editor::set_selection_from_range (Location& loc)
1571 begin_reversible_command (_("set selection from range"));
1572 selection->set (loc.start(), loc.end());
1573 commit_reversible_command ();
1575 if (!Profile->get_sae()) {
1576 set_mouse_mode (Editing::MouseRange, false);
1581 Editor::select_all_selectables_using_time_selection ()
1583 list<Selectable *> touched;
1585 if (selection->time.empty()) {
1589 framepos_t start = selection->time[clicked_selection].start;
1590 framepos_t end = selection->time[clicked_selection].end;
1592 if (end - start < 1) {
1598 if (selection->tracks.empty()) {
1601 ts = &selection->tracks;
1604 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1605 if ((*iter)->hidden()) {
1608 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1611 begin_reversible_command (_("select all from range"));
1612 selection->set (touched);
1613 commit_reversible_command ();
1618 Editor::select_all_selectables_using_punch()
1620 Location* location = _session->locations()->auto_punch_location();
1621 list<Selectable *> touched;
1623 if (location == 0 || (location->end() - location->start() <= 1)) {
1630 if (selection->tracks.empty()) {
1633 ts = &selection->tracks;
1636 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1637 if ((*iter)->hidden()) {
1640 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1642 begin_reversible_command (_("select all from punch"));
1643 selection->set (touched);
1644 commit_reversible_command ();
1649 Editor::select_all_selectables_using_loop()
1651 Location* location = _session->locations()->auto_loop_location();
1652 list<Selectable *> touched;
1654 if (location == 0 || (location->end() - location->start() <= 1)) {
1661 if (selection->tracks.empty()) {
1664 ts = &selection->tracks;
1667 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1668 if ((*iter)->hidden()) {
1671 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1673 begin_reversible_command (_("select all from loop"));
1674 selection->set (touched);
1675 commit_reversible_command ();
1680 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1684 list<Selectable *> touched;
1687 start = cursor->current_frame();
1688 end = _session->current_end_frame();
1690 if (cursor->current_frame() > 0) {
1692 end = cursor->current_frame() - 1;
1698 if (internal_editing()) {
1699 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1700 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1702 mrv->select_range (start, end);
1709 begin_reversible_command (_("select all after cursor"));
1711 begin_reversible_command (_("select all before cursor"));
1716 if (selection->tracks.empty()) {
1719 ts = &selection->tracks;
1722 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1723 if ((*iter)->hidden()) {
1726 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1728 selection->set (touched);
1729 commit_reversible_command ();
1733 Editor::select_all_selectables_using_edit (bool after)
1737 list<Selectable *> touched;
1740 start = get_preferred_edit_position();
1741 end = _session->current_end_frame();
1743 if ((end = get_preferred_edit_position()) > 1) {
1751 if (internal_editing()) {
1752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1753 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1754 mrv->select_range (start, end);
1760 begin_reversible_command (_("select all after edit"));
1762 begin_reversible_command (_("select all before edit"));
1767 if (selection->tracks.empty()) {
1770 ts = &selection->tracks;
1773 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1774 if ((*iter)->hidden()) {
1777 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1779 selection->set (touched);
1780 commit_reversible_command ();
1784 Editor::select_all_selectables_between (bool /*within*/)
1788 list<Selectable *> touched;
1790 if (!get_edit_op_range (start, end)) {
1794 if (internal_editing()) {
1795 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1796 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1797 mrv->select_range (start, end);
1804 if (selection->tracks.empty()) {
1807 ts = &selection->tracks;
1810 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1811 if ((*iter)->hidden()) {
1814 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1817 selection->set (touched);
1821 Editor::select_range_between ()
1826 if ( !selection->time.empty() ) {
1827 selection->clear_time ();
1830 if (!get_edit_op_range (start, end)) {
1834 set_mouse_mode (MouseRange);
1835 selection->set (start, end);
1839 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1844 /* if an explicit range exists, use it */
1846 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1847 /* we know that these are ordered */
1848 start = selection->time.start();
1849 end = selection->time.end_frame();
1857 // if (!mouse_frame (m, ignored)) {
1858 // /* mouse is not in a canvas, try playhead+selected marker.
1859 // this is probably most true when using menus.
1862 // if (selection->markers.empty()) {
1866 // start = selection->markers.front()->position();
1867 // end = _session->audible_frame();
1871 // switch (_edit_point) {
1872 // case EditAtPlayhead:
1873 // if (selection->markers.empty()) {
1874 // /* use mouse + playhead */
1876 // end = _session->audible_frame();
1878 // /* use playhead + selected marker */
1879 // start = _session->audible_frame();
1880 // end = selection->markers.front()->position();
1884 // case EditAtMouse:
1885 // /* use mouse + selected marker */
1886 // if (selection->markers.empty()) {
1888 // end = _session->audible_frame();
1890 // start = selection->markers.front()->position();
1895 // case EditAtSelectedMarker:
1896 // /* use mouse + selected marker */
1897 // if (selection->markers.empty()) {
1899 // MessageDialog win (_("No edit range defined"),
1904 // win.set_secondary_text (
1905 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1908 // win.set_default_response (RESPONSE_CLOSE);
1909 // win.set_position (Gtk::WIN_POS_MOUSE);
1914 // return false; // NO RANGE
1916 // start = selection->markers.front()->position();
1922 // if (start == end) {
1926 // if (start > end) {
1927 // swap (start, end);
1930 /* turn range into one delimited by start...end,
1940 Editor::deselect_all ()
1942 selection->clear ();
1946 Editor::select_range (framepos_t s, framepos_t e)
1948 selection->add (clicked_axisview);
1949 selection->time.clear ();
1950 return selection->set (s, e);