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);
210 selection->remove (clicked_axisview);
213 if (arg.is_select() && arg.is_active()) {
214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217 } else if (group && group->is_active()) {
218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 if ((*i)->route_group() == group) {
224 selection->add (clicked_axisview);
230 if (!had_tracks && arg.is_select() && arg.is_active()) {
231 /* nothing was selected already, and all group is active etc. so use
234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
237 } else if (group && group->is_active()) {
238 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 if ((*i)->route_group() == group) {
244 selection->add (clicked_axisview);
250 if (!had_tracks && arg.is_select() && arg.is_active()) {
251 /* nothing was selected already, and all group is active etc. so use
254 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
257 } else if (group && group->is_active()) {
258 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
259 if ((*i)->route_group() == group) {
264 selection->set (clicked_axisview);
268 case Selection::Extend:
275 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
277 begin_reversible_selection_op (X_("Set Selected Track"));
280 case Selection::Toggle:
281 if (selection->selected (&view)) {
283 selection->remove (&view);
286 selection->add (&view);
291 if (!selection->selected (&view)) {
292 selection->add (&view);
297 selection->set (&view);
300 case Selection::Extend:
301 extend_selection_to_track (view);
305 commit_reversible_selection_op ();
309 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
311 if (!clicked_routeview) {
319 set_selected_track (*clicked_routeview, op, no_remove);
323 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
325 if (!clicked_control_point) {
333 selection->set (clicked_control_point);
339 selection->add (clicked_control_point);
343 case Selection::Toggle:
345 /* This is a bit of a hack; if we Primary-Click-Drag a control
346 point (for push drag) we want the point we clicked on to be
347 selected, otherwise we end up confusingly dragging an
348 unselected point. So here we ensure that the point is selected
349 after the press, and if we subsequently get a release (meaning no
350 drag occurred) we set things up so that the toggle has happened.
352 if (press && !selection->selected (clicked_control_point)) {
353 /* This is the button press, and the control point is not selected; make it so,
354 in case this press leads to a drag. Also note that having done this, we don't
355 need to toggle again on release.
357 selection->toggle (clicked_control_point);
358 _control_point_toggled_on_press = true;
360 } else if (!press && !_control_point_toggled_on_press) {
361 /* This is the release, and the point wasn't toggled on the press, so do it now */
362 selection->toggle (clicked_control_point);
366 _control_point_toggled_on_press = false;
369 case Selection::Extend:
378 Editor::get_onscreen_tracks (TrackViewList& tvl)
380 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
381 if ((*i)->y_position() < _visible_canvas_height) {
387 /** Call a slot for a given `basis' track and also for any track that is in the same
388 * active route group with a particular set of properties.
390 * @param sl Slot to call.
391 * @param basis Basis track.
392 * @param prop Properties that active edit groups must share to be included in the map.
396 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
398 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
400 if (route_basis == 0) {
404 set<RouteTimeAxisView*> tracks;
405 tracks.insert (route_basis);
407 RouteGroup* group = route_basis->route()->route_group();
409 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
411 /* the basis is a member of an active route group, with the appropriate
412 properties; find other members */
414 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
415 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
416 if (v && v->route()->route_group() == group) {
423 uint32_t const sz = tracks.size ();
425 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
430 /** Call a slot for a given `basis' track and also for any track that is in the same
431 * active route group with a particular set of properties.
433 * @param sl Slot to call.
434 * @param basis Basis track.
435 * @param prop Properties that active edit groups must share to be included in the map.
439 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
441 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
442 set<boost::shared_ptr<Playlist> > playlists;
444 if (route_basis == 0) {
448 set<RouteTimeAxisView*> tracks;
449 tracks.insert (route_basis);
451 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
453 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
455 /* the basis is a member of an active route group, with the appropriate
456 properties; find other members */
458 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
459 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
461 if (v && v->route()->route_group() == group) {
463 boost::shared_ptr<Track> t = v->track();
465 if (playlists.insert (t->playlist()).second) {
466 /* haven't seen this playlist yet */
470 /* not actually a "Track", but a timeaxis view that
471 we should mapover anyway.
480 uint32_t const sz = tracks.size ();
482 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
488 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
490 boost::shared_ptr<Playlist> pl;
491 vector<boost::shared_ptr<Region> > results;
493 boost::shared_ptr<Track> tr;
495 if ((tr = tv.track()) == 0) {
500 if (&tv == &basis->get_time_axis_view()) {
501 /* looking in same track as the original */
505 if ((pl = tr->playlist()) != 0) {
506 pl->get_equivalent_regions (basis->region(), results);
509 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
510 if ((marv = tv.view()->find_view (*ir)) != 0) {
511 all_equivs->push_back (marv);
517 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
519 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);
521 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
523 equivalent_regions.push_back (basis);
527 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
529 RegionSelection equivalent;
531 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
533 vector<RegionView*> eq;
535 mapover_tracks_with_unique_playlists (
536 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
537 &(*i)->get_time_axis_view(), prop);
539 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
550 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
552 int region_count = 0;
554 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
556 RouteTimeAxisView* tatv;
558 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
560 boost::shared_ptr<Playlist> pl;
561 vector<boost::shared_ptr<Region> > results;
563 boost::shared_ptr<Track> tr;
565 if ((tr = tatv->track()) == 0) {
570 if ((pl = (tr->playlist())) != 0) {
571 pl->get_region_list_equivalent_regions (region, results);
574 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
575 if ((marv = tatv->view()->find_view (*ir)) != 0) {
588 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
590 vector<RegionView*> all_equivalent_regions;
593 if (!clicked_regionview || !clicked_routeview) {
598 button_release_can_deselect = false;
601 if (op == Selection::Toggle || op == Selection::Set) {
604 case Selection::Toggle:
605 if (selection->selected (clicked_regionview)) {
608 /* whatever was clicked was selected already; do nothing here but allow
609 the button release to deselect it
612 button_release_can_deselect = true;
615 if (button_release_can_deselect) {
617 /* just remove this one region, but only on a permitted button release */
619 selection->remove (clicked_regionview);
622 /* no more deselect action on button release till a new press
623 finds an already selected object.
626 button_release_can_deselect = false;
634 if (selection->selected (clicked_routeview)) {
635 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
637 all_equivalent_regions.push_back (clicked_regionview);
640 /* add all the equivalent regions, but only on button press */
642 if (!all_equivalent_regions.empty()) {
646 selection->add (all_equivalent_regions);
652 if (!selection->selected (clicked_regionview)) {
653 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
654 selection->set (all_equivalent_regions);
657 /* clicked on an already selected region */
661 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
662 selection->set(all_equivalent_regions);
673 } else if (op == Selection::Extend) {
675 list<Selectable*> results;
676 framepos_t last_frame;
677 framepos_t first_frame;
678 bool same_track = false;
680 /* 1. find the last selected regionview in the track that was clicked in */
683 first_frame = max_framepos;
685 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
686 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
688 if ((*x)->region()->last_frame() > last_frame) {
689 last_frame = (*x)->region()->last_frame();
692 if ((*x)->region()->first_frame() < first_frame) {
693 first_frame = (*x)->region()->first_frame();
702 /* 2. figure out the boundaries for our search for new objects */
704 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
705 case Evoral::OverlapNone:
706 if (last_frame < clicked_regionview->region()->first_frame()) {
707 first_frame = last_frame;
708 last_frame = clicked_regionview->region()->last_frame();
710 last_frame = first_frame;
711 first_frame = clicked_regionview->region()->first_frame();
715 case Evoral::OverlapExternal:
716 if (last_frame < clicked_regionview->region()->first_frame()) {
717 first_frame = last_frame;
718 last_frame = clicked_regionview->region()->last_frame();
720 last_frame = first_frame;
721 first_frame = clicked_regionview->region()->first_frame();
725 case Evoral::OverlapInternal:
726 if (last_frame < clicked_regionview->region()->first_frame()) {
727 first_frame = last_frame;
728 last_frame = clicked_regionview->region()->last_frame();
730 last_frame = first_frame;
731 first_frame = clicked_regionview->region()->first_frame();
735 case Evoral::OverlapStart:
736 case Evoral::OverlapEnd:
737 /* nothing to do except add clicked region to selection, since it
738 overlaps with the existing selection in this track.
745 /* click in a track that has no regions selected, so extend vertically
746 to pick out all regions that are defined by the existing selection
751 first_frame = clicked_regionview->region()->position();
752 last_frame = clicked_regionview->region()->last_frame();
754 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
755 if ((*i)->region()->position() < first_frame) {
756 first_frame = (*i)->region()->position();
758 if ((*i)->region()->last_frame() + 1 > last_frame) {
759 last_frame = (*i)->region()->last_frame();
764 /* 2. find all the tracks we should select in */
766 set<RouteTimeAxisView*> relevant_tracks;
768 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
769 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
771 relevant_tracks.insert (r);
775 set<RouteTimeAxisView*> already_in_selection;
777 if (relevant_tracks.empty()) {
779 /* no tracks selected .. thus .. if the
780 regionview we're in isn't selected
781 (i.e. we're about to extend to it), then
782 find all tracks between the this one and
786 if (!selection->selected (clicked_regionview)) {
788 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
792 /* add this track to the ones we will search */
794 relevant_tracks.insert (rtv);
796 /* find the track closest to this one that
797 already a selected region.
800 RouteTimeAxisView* closest = 0;
801 int distance = INT_MAX;
802 int key = rtv->route()->order_key ();
804 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
806 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
808 if (artv && artv != rtv) {
810 pair<set<RouteTimeAxisView*>::iterator,bool> result;
812 result = already_in_selection.insert (artv);
815 /* newly added to already_in_selection */
817 int d = artv->route()->order_key ();
821 if (abs (d) < distance) {
831 /* now add all tracks between that one and this one */
833 int okey = closest->route()->order_key ();
839 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
840 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
841 if (artv && artv != rtv) {
843 int k = artv->route()->order_key ();
845 if (k >= okey && k <= key) {
847 /* in range but don't add it if
848 it already has tracks selected.
849 this avoids odd selection
850 behaviour that feels wrong.
853 if (find (already_in_selection.begin(),
854 already_in_selection.end(),
855 artv) == already_in_selection.end()) {
857 relevant_tracks.insert (artv);
867 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
868 one that was clicked.
871 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
872 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
875 /* 4. convert to a vector of regions */
877 vector<RegionView*> regions;
879 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
882 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
883 regions.push_back (arv);
887 if (!regions.empty()) {
888 selection->add (regions);
899 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
901 vector<RegionView*> all_equivalent_regions;
903 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
905 if (all_equivalent_regions.empty()) {
909 begin_reversible_selection_op (X_("set selected regions"));
912 case Selection::Toggle:
913 /* XXX this is not correct */
914 selection->toggle (all_equivalent_regions);
917 selection->set (all_equivalent_regions);
919 case Selection::Extend:
920 selection->add (all_equivalent_regions);
923 selection->add (all_equivalent_regions);
927 commit_reversible_selection_op () ;
931 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
934 boost::shared_ptr<Region> r (weak_r.lock());
940 if ((rv = sv->find_view (r)) == 0) {
944 /* don't reset the selection if its something other than
945 a single other region.
948 if (selection->regions.size() > 1) {
952 begin_reversible_selection_op (X_("set selected regions"));
956 commit_reversible_selection_op () ;
962 Editor::track_selection_changed ()
964 switch (selection->tracks.size()) {
968 set_selected_mixer_strip (*(selection->tracks.front()));
972 RouteNotificationListPtr routes (new RouteNotificationList);
974 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
976 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
978 (*i)->set_selected (yn);
980 TimeAxisView::Children c = (*i)->get_child_list ();
981 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
982 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
986 (*i)->reshow_selection (selection->time);
988 (*i)->hide_selection ();
993 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
995 routes->push_back (rtav->route());
1000 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1002 /* notify control protocols */
1004 ControlProtocol::TrackSelectionChanged (routes);
1008 Editor::time_selection_changed ()
1010 if (Profile->get_sae()) {
1014 /* XXX this is superficially inefficient. Hide the selection in all
1015 * tracks, then show it in all selected tracks.
1017 * However, if you investigate what this actually does, it isn't
1018 * anywhere nearly as bad as it may appear. Remember: nothing is
1019 * redrawn or even recomputed during these two loops - that only
1020 * happens when we next render ...
1023 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1024 (*i)->hide_selection ();
1027 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1028 (*i)->show_selection (selection->time);
1031 if (selection->time.empty()) {
1032 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1034 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1038 /** Set all region actions to have a given sensitivity */
1040 Editor::sensitize_all_region_actions (bool s)
1042 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1044 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1045 (*i)->set_sensitive (s);
1048 _all_region_actions_sensitized = s;
1051 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1052 * This method should be called just before displaying a Region menu. When a Region menu is not
1053 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1054 * on entered_regionviews work without having to check sensitivity every time the selection or
1055 * entered_regionview changes.
1057 * This method also sets up toggle action state as appropriate.
1060 Editor::sensitize_the_right_region_actions ()
1063 RegionSelection rs = get_regions_from_selection_and_entered ();
1064 sensitize_all_region_actions (!rs.empty ());
1066 _ignore_region_action = true;
1068 /* Look through the regions that are selected and make notes about what we have got */
1070 bool have_audio = false;
1071 bool have_multichannel_audio = false;
1072 bool have_midi = false;
1073 bool have_locked = false;
1074 bool have_unlocked = false;
1075 bool have_video_locked = false;
1076 bool have_video_unlocked = false;
1077 bool have_position_lock_style_audio = false;
1078 bool have_position_lock_style_music = false;
1079 bool have_muted = false;
1080 bool have_unmuted = false;
1081 bool have_opaque = false;
1082 bool have_non_opaque = false;
1083 bool have_not_at_natural_position = false;
1084 bool have_envelope_active = false;
1085 bool have_envelope_inactive = false;
1086 bool have_non_unity_scale_amplitude = false;
1087 bool have_compound_regions = false;
1088 bool have_inactive_fade_in = false;
1089 bool have_inactive_fade_out = false;
1090 bool have_active_fade_in = false;
1091 bool have_active_fade_out = false;
1093 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1095 boost::shared_ptr<Region> r = (*i)->region ();
1096 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1100 if (ar->n_channels() > 1) {
1101 have_multichannel_audio = true;
1105 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1109 if (r->is_compound()) {
1110 have_compound_regions = true;
1116 have_unlocked = true;
1119 if (r->video_locked()) {
1120 have_video_locked = true;
1122 have_video_unlocked = true;
1125 if (r->position_lock_style() == MusicTime) {
1126 have_position_lock_style_music = true;
1128 have_position_lock_style_audio = true;
1134 have_unmuted = true;
1140 have_non_opaque = true;
1143 if (!r->at_natural_position()) {
1144 have_not_at_natural_position = true;
1148 if (ar->envelope_active()) {
1149 have_envelope_active = true;
1151 have_envelope_inactive = true;
1154 if (ar->scale_amplitude() != 1) {
1155 have_non_unity_scale_amplitude = true;
1158 if (ar->fade_in_active ()) {
1159 have_active_fade_in = true;
1161 have_inactive_fade_in = true;
1164 if (ar->fade_out_active ()) {
1165 have_active_fade_out = true;
1167 have_inactive_fade_out = true;
1172 if (rs.size() > 1) {
1173 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1174 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1175 _region_actions->get_action("rename-region")->set_sensitive (false);
1177 /* XXX need to check whether there is than 1 per
1178 playlist, because otherwise this makes no sense.
1180 _region_actions->get_action("combine-regions")->set_sensitive (true);
1182 _region_actions->get_action("combine-regions")->set_sensitive (false);
1184 } else if (rs.size() == 1) {
1185 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1186 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1187 _region_actions->get_action("combine-regions")->set_sensitive (false);
1190 if (!have_multichannel_audio) {
1191 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1195 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1196 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1197 _region_actions->get_action("quantize-region")->set_sensitive (false);
1198 _region_actions->get_action("legatize-region")->set_sensitive (false);
1199 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1200 _region_actions->get_action("fork-region")->set_sensitive (false);
1201 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1202 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1203 _region_actions->get_action("transpose-region")->set_sensitive (false);
1205 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1206 /* others were already marked sensitive */
1209 if (_edit_point == EditAtMouse) {
1210 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1211 _region_actions->get_action("trim-front")->set_sensitive (false);
1212 _region_actions->get_action("trim-back")->set_sensitive (false);
1213 _region_actions->get_action("split-region")->set_sensitive (false);
1214 _region_actions->get_action("place-transient")->set_sensitive (false);
1217 if (have_compound_regions) {
1218 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1220 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1225 if (have_envelope_active && !have_envelope_inactive) {
1226 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1227 } else if (have_envelope_active && have_envelope_inactive) {
1228 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1233 _region_actions->get_action("analyze-region")->set_sensitive (false);
1234 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1235 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1236 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1240 if (!have_non_unity_scale_amplitude || !have_audio) {
1241 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1244 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1245 a->set_active (have_locked && !have_unlocked);
1246 if (have_locked && have_unlocked) {
1247 // a->set_inconsistent ();
1250 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1251 a->set_active (have_video_locked && !have_video_unlocked);
1252 if (have_video_locked && have_video_unlocked) {
1253 // a->set_inconsistent ();
1256 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1257 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1259 if (have_position_lock_style_music && have_position_lock_style_audio) {
1260 // a->set_inconsistent ();
1263 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1264 a->set_active (have_muted && !have_unmuted);
1265 if (have_muted && have_unmuted) {
1266 // a->set_inconsistent ();
1269 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1270 a->set_active (have_opaque && !have_non_opaque);
1271 if (have_opaque && have_non_opaque) {
1272 // a->set_inconsistent ();
1275 if (!have_not_at_natural_position) {
1276 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1279 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1280 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1281 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1283 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1286 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1287 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1288 if (have_active_fade_in && have_inactive_fade_in) {
1289 // a->set_inconsistent ();
1292 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1293 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1295 if (have_active_fade_out && have_inactive_fade_out) {
1296 // a->set_inconsistent ();
1299 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1300 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1302 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1303 a->set_active (have_active_fade && !have_inactive_fade);
1305 if (have_active_fade && have_inactive_fade) {
1306 // a->set_inconsistent ();
1309 _ignore_region_action = false;
1311 _all_region_actions_sensitized = false;
1316 Editor::region_selection_changed ()
1318 _regions->block_change_connection (true);
1319 editor_regions_selection_changed_connection.block(true);
1321 if (_region_selection_change_updates_region_list) {
1322 _regions->unselect_all ();
1325 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1326 (*i)->set_selected_regionviews (selection->regions);
1329 if (_region_selection_change_updates_region_list) {
1330 _regions->set_selected (selection->regions);
1333 _regions->block_change_connection (false);
1334 editor_regions_selection_changed_connection.block(false);
1336 if (!_all_region_actions_sensitized) {
1337 /* This selection change might have changed what region actions
1338 are allowed, so sensitize them all in case a key is pressed.
1340 sensitize_all_region_actions (true);
1343 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1344 maybe_locate_with_edit_preroll (selection->regions.start());
1349 Editor::point_selection_changed ()
1351 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1352 (*i)->set_selected_points (selection->points);
1357 Editor::select_all_in_track (Selection::Operation op)
1359 list<Selectable *> touched;
1361 if (!clicked_routeview) {
1365 begin_reversible_selection_op (X_("Select All in Track"));
1367 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1370 case Selection::Toggle:
1371 selection->add (touched);
1373 case Selection::Set:
1374 selection->set (touched);
1376 case Selection::Extend:
1377 /* meaningless, because we're selecting everything */
1379 case Selection::Add:
1380 selection->add (touched);
1384 commit_reversible_selection_op ();
1388 Editor::select_all_internal_edit (Selection::Operation)
1390 bool selected = false;
1392 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1393 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1395 mrv->select_all_notes ();
1400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1402 mrv->select_all_notes ();
1410 Editor::select_all_objects (Selection::Operation op)
1412 list<Selectable *> touched;
1414 TrackViewList ts = track_views;
1416 if (internal_editing() && select_all_internal_edit(op)) {
1417 return; // Selected notes
1420 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1421 if ((*iter)->hidden()) {
1424 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1425 selection->add (*iter);
1429 begin_reversible_selection_op (X_("select all"));
1431 case Selection::Add:
1432 selection->add (touched);
1434 case Selection::Toggle:
1435 selection->add (touched);
1437 case Selection::Set:
1438 selection->set (touched);
1440 case Selection::Extend:
1441 /* meaningless, because we're selecting everything */
1444 commit_reversible_selection_op ();
1448 Editor::invert_selection_in_track ()
1450 list<Selectable *> touched;
1452 if (!clicked_routeview) {
1456 begin_reversible_selection_op (X_("Invert Selection in Track"));
1457 clicked_routeview->get_inverted_selectables (*selection, touched);
1458 selection->set (touched);
1459 commit_reversible_selection_op ();
1463 Editor::invert_selection ()
1465 list<Selectable *> touched;
1467 if (internal_editing()) {
1468 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1469 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1471 mrv->invert_selection ();
1477 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1478 if ((*iter)->hidden()) {
1481 (*iter)->get_inverted_selectables (*selection, touched);
1484 begin_reversible_selection_op (X_("Invert Selection"));
1485 selection->set (touched);
1486 commit_reversible_selection_op ();
1489 /** @param start Start time in session frames.
1490 * @param end End time in session frames.
1491 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1492 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1493 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1494 * within the region are already selected.
1497 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1499 list<Selectable*> found;
1501 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1503 if ((*iter)->hidden()) {
1507 (*iter)->get_selectables (start, end, top, bot, found);
1510 if (found.empty()) {
1511 selection->clear_objects();
1512 selection->clear_time ();
1516 if (preserve_if_selected && op != Selection::Toggle) {
1517 list<Selectable*>::iterator i = found.begin();
1518 while (i != found.end() && (*i)->get_selected()) {
1522 if (i == found.end()) {
1527 begin_reversible_selection_op (X_("select all within"));
1529 case Selection::Add:
1530 selection->add (found);
1532 case Selection::Toggle:
1533 selection->toggle (found);
1535 case Selection::Set:
1536 selection->set (found);
1538 case Selection::Extend:
1539 /* not defined yet */
1543 commit_reversible_selection_op ();
1547 Editor::set_selection_from_region ()
1549 if (selection->regions.empty()) {
1553 /* find all the tracks that have selected regions */
1555 set<TimeAxisView*> tracks;
1557 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1558 tracks.insert (&(*r)->get_time_axis_view());
1562 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1564 /* select range (this will clear the region selection) */
1566 selection->set (selection->regions.start(), selection->regions.end_frame());
1568 /* and select the tracks */
1570 selection->set (tvl);
1572 if (!Profile->get_sae()) {
1573 set_mouse_mode (Editing::MouseRange, false);
1578 Editor::set_selection_from_punch()
1582 if ((location = _session->locations()->auto_punch_location()) == 0) {
1586 set_selection_from_range (*location);
1590 Editor::set_selection_from_loop()
1594 if ((location = _session->locations()->auto_loop_location()) == 0) {
1597 set_selection_from_range (*location);
1601 Editor::set_selection_from_range (Location& loc)
1603 begin_reversible_selection_op (X_("set selection from range"));
1604 selection->set (loc.start(), loc.end());
1605 commit_reversible_selection_op ();
1607 if (!Profile->get_sae()) {
1608 set_mouse_mode (Editing::MouseRange, false);
1613 Editor::select_all_selectables_using_time_selection ()
1615 list<Selectable *> touched;
1617 if (selection->time.empty()) {
1621 framepos_t start = selection->time[clicked_selection].start;
1622 framepos_t end = selection->time[clicked_selection].end;
1624 if (end - 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 (start, end - 1, 0, DBL_MAX, touched);
1643 begin_reversible_selection_op (X_("select all from range"));
1644 selection->set (touched);
1645 commit_reversible_selection_op ();
1650 Editor::select_all_selectables_using_punch()
1652 Location* location = _session->locations()->auto_punch_location();
1653 list<Selectable *> touched;
1655 if (location == 0 || (location->end() - location->start() <= 1)) {
1662 if (selection->tracks.empty()) {
1665 ts = &selection->tracks;
1668 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1669 if ((*iter)->hidden()) {
1672 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1674 begin_reversible_selection_op (X_("select all from punch"));
1675 selection->set (touched);
1676 commit_reversible_selection_op ();
1681 Editor::select_all_selectables_using_loop()
1683 Location* location = _session->locations()->auto_loop_location();
1684 list<Selectable *> touched;
1686 if (location == 0 || (location->end() - location->start() <= 1)) {
1693 if (selection->tracks.empty()) {
1696 ts = &selection->tracks;
1699 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1700 if ((*iter)->hidden()) {
1703 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1705 begin_reversible_selection_op (X_("select all from loop"));
1706 selection->set (touched);
1707 commit_reversible_selection_op ();
1712 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1716 list<Selectable *> touched;
1719 start = cursor->current_frame();
1720 end = _session->current_end_frame();
1722 if (cursor->current_frame() > 0) {
1724 end = cursor->current_frame() - 1;
1730 if (internal_editing()) {
1731 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1732 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1734 mrv->select_range (start, end);
1741 begin_reversible_selection_op (X_("select all after cursor"));
1743 begin_reversible_selection_op (X_("select all before cursor"));
1748 if (selection->tracks.empty()) {
1751 ts = &selection->tracks;
1754 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1755 if ((*iter)->hidden()) {
1758 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1760 selection->set (touched);
1761 commit_reversible_selection_op ();
1765 Editor::select_all_selectables_using_edit (bool after)
1769 list<Selectable *> touched;
1772 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1773 end = _session->current_end_frame();
1775 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1783 if (internal_editing()) {
1784 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1785 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1786 mrv->select_range (start, end);
1792 begin_reversible_selection_op (X_("select all after edit"));
1794 begin_reversible_selection_op (X_("select all before edit"));
1799 if (selection->tracks.empty()) {
1802 ts = &selection->tracks;
1805 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1806 if ((*iter)->hidden()) {
1809 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1811 selection->set (touched);
1812 commit_reversible_selection_op ();
1816 Editor::select_all_selectables_between (bool within)
1820 list<Selectable *> touched;
1822 if (!get_edit_op_range (start, end)) {
1826 if (internal_editing()) {
1827 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1828 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1829 mrv->select_range (start, end);
1836 if (selection->tracks.empty()) {
1839 ts = &selection->tracks;
1842 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1843 if ((*iter)->hidden()) {
1846 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1849 begin_reversible_selection_op (X_("Select all Selectables Between"));
1850 selection->set (touched);
1851 commit_reversible_selection_op ();
1855 Editor::select_range_between ()
1860 if ( !selection->time.empty() ) {
1861 selection->clear_time ();
1864 if (!get_edit_op_range (start, end)) {
1868 begin_reversible_selection_op (X_("Select Range Between"));
1869 set_mouse_mode (MouseRange);
1870 selection->set (start, end);
1871 commit_reversible_selection_op ();
1875 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1880 /* if an explicit range exists, use it */
1882 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1883 /* we know that these are ordered */
1884 start = selection->time.start();
1885 end = selection->time.end_frame();
1893 // if (!mouse_frame (m, ignored)) {
1894 // /* mouse is not in a canvas, try playhead+selected marker.
1895 // this is probably most true when using menus.
1898 // if (selection->markers.empty()) {
1902 // start = selection->markers.front()->position();
1903 // end = _session->audible_frame();
1907 // switch (_edit_point) {
1908 // case EditAtPlayhead:
1909 // if (selection->markers.empty()) {
1910 // /* use mouse + playhead */
1912 // end = _session->audible_frame();
1914 // /* use playhead + selected marker */
1915 // start = _session->audible_frame();
1916 // end = selection->markers.front()->position();
1920 // case EditAtMouse:
1921 // /* use mouse + selected marker */
1922 // if (selection->markers.empty()) {
1924 // end = _session->audible_frame();
1926 // start = selection->markers.front()->position();
1931 // case EditAtSelectedMarker:
1932 // /* use mouse + selected marker */
1933 // if (selection->markers.empty()) {
1935 // MessageDialog win (_("No edit range defined"),
1940 // win.set_secondary_text (
1941 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1944 // win.set_default_response (RESPONSE_CLOSE);
1945 // win.set_position (Gtk::WIN_POS_MOUSE);
1950 // return false; // NO RANGE
1952 // start = selection->markers.front()->position();
1958 // if (start == end) {
1962 // if (start > end) {
1963 // swap (start, end);
1966 /* turn range into one delimited by start...end,
1976 Editor::deselect_all ()
1978 begin_reversible_selection_op (X_("Deselect All"));
1979 selection->clear ();
1980 commit_reversible_selection_op ();
1984 Editor::select_range (framepos_t s, framepos_t e)
1986 begin_reversible_selection_op (X_("Select Range"));
1987 selection->add (clicked_axisview);
1988 selection->time.clear ();
1989 long ret = selection->set (s, e);
1990 commit_reversible_selection_op ();