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)
273 begin_reversible_selection_op(_("Set Selected Track"));
276 case Selection::Toggle:
277 if (selection->selected (&view)) {
279 selection->remove (&view);
282 selection->add (&view);
287 if (!selection->selected (&view)) {
288 selection->add (&view);
293 selection->set (&view);
296 case Selection::Extend:
297 extend_selection_to_track (view);
301 commit_reversible_selection_op ();
305 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
307 if (!clicked_routeview) {
315 set_selected_track (*clicked_routeview, op, no_remove);
319 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
321 if (!clicked_control_point) {
328 selection->set (clicked_control_point);
333 selection->add (clicked_control_point);
336 case Selection::Toggle:
337 /* This is a bit of a hack; if we Primary-Click-Drag a control
338 point (for push drag) we want the point we clicked on to be
339 selected, otherwise we end up confusingly dragging an
340 unselected point. So here we ensure that the point is selected
341 after the press, and if we subsequently get a release (meaning no
342 drag occurred) we set things up so that the toggle has happened.
344 if (press && !selection->selected (clicked_control_point)) {
345 /* This is the button press, and the control point is not selected; make it so,
346 in case this press leads to a drag. Also note that having done this, we don't
347 need to toggle again on release.
349 selection->toggle (clicked_control_point);
350 _control_point_toggled_on_press = true;
351 } else if (!press && !_control_point_toggled_on_press) {
352 /* This is the release, and the point wasn't toggled on the press, so do it now */
353 selection->toggle (clicked_control_point);
356 _control_point_toggled_on_press = false;
359 case Selection::Extend:
368 Editor::get_onscreen_tracks (TrackViewList& tvl)
370 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
371 if ((*i)->y_position() < _visible_canvas_height) {
377 /** Call a slot for a given `basis' track and also for any track that is in the same
378 * active route group with a particular set of properties.
380 * @param sl Slot to call.
381 * @param basis Basis track.
382 * @param prop Properties that active edit groups must share to be included in the map.
386 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
388 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
390 if (route_basis == 0) {
394 set<RouteTimeAxisView*> tracks;
395 tracks.insert (route_basis);
397 RouteGroup* group = route_basis->route()->route_group();
399 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
401 /* the basis is a member of an active route group, with the appropriate
402 properties; find other members */
404 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
405 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
406 if (v && v->route()->route_group() == group) {
413 uint32_t const sz = tracks.size ();
415 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
420 /** Call a slot for a given `basis' track and also for any track that is in the same
421 * active route group with a particular set of properties.
423 * @param sl Slot to call.
424 * @param basis Basis track.
425 * @param prop Properties that active edit groups must share to be included in the map.
429 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
431 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
432 set<boost::shared_ptr<Playlist> > playlists;
434 if (route_basis == 0) {
438 set<RouteTimeAxisView*> tracks;
439 tracks.insert (route_basis);
441 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
443 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
445 /* the basis is a member of an active route group, with the appropriate
446 properties; find other members */
448 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
449 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
451 if (v && v->route()->route_group() == group) {
453 boost::shared_ptr<Track> t = v->track();
455 if (playlists.insert (t->playlist()).second) {
456 /* haven't seen this playlist yet */
460 /* not actually a "Track", but a timeaxis view that
461 we should mapover anyway.
470 uint32_t const sz = tracks.size ();
472 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
478 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
480 boost::shared_ptr<Playlist> pl;
481 vector<boost::shared_ptr<Region> > results;
483 boost::shared_ptr<Track> tr;
485 if ((tr = tv.track()) == 0) {
490 if (&tv == &basis->get_time_axis_view()) {
491 /* looking in same track as the original */
495 if ((pl = tr->playlist()) != 0) {
496 pl->get_equivalent_regions (basis->region(), results);
499 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
500 if ((marv = tv.view()->find_view (*ir)) != 0) {
501 all_equivs->push_back (marv);
507 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
509 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);
511 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
513 equivalent_regions.push_back (basis);
517 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
519 RegionSelection equivalent;
521 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
523 vector<RegionView*> eq;
525 mapover_tracks_with_unique_playlists (
526 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
527 &(*i)->get_time_axis_view(), prop);
529 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
540 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
542 int region_count = 0;
544 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
546 RouteTimeAxisView* tatv;
548 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
550 boost::shared_ptr<Playlist> pl;
551 vector<boost::shared_ptr<Region> > results;
553 boost::shared_ptr<Track> tr;
555 if ((tr = tatv->track()) == 0) {
560 if ((pl = (tr->playlist())) != 0) {
561 pl->get_region_list_equivalent_regions (region, results);
564 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
565 if ((marv = tatv->view()->find_view (*ir)) != 0) {
578 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
580 vector<RegionView*> all_equivalent_regions;
583 if (!clicked_regionview || !clicked_routeview) {
588 button_release_can_deselect = false;
591 if (op == Selection::Toggle || op == Selection::Set) {
594 case Selection::Toggle:
595 if (selection->selected (clicked_regionview)) {
598 /* whatever was clicked was selected already; do nothing here but allow
599 the button release to deselect it
602 button_release_can_deselect = true;
605 if (button_release_can_deselect) {
607 /* just remove this one region, but only on a permitted button release */
609 selection->remove (clicked_regionview);
612 /* no more deselect action on button release till a new press
613 finds an already selected object.
616 button_release_can_deselect = false;
624 if (selection->selected (clicked_routeview)) {
625 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
627 all_equivalent_regions.push_back (clicked_regionview);
630 /* add all the equivalent regions, but only on button press */
632 if (!all_equivalent_regions.empty()) {
636 selection->add (all_equivalent_regions);
642 if (!selection->selected (clicked_regionview)) {
643 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
644 selection->set (all_equivalent_regions);
647 /* clicked on an already selected region */
651 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
652 selection->set(all_equivalent_regions);
663 } else if (op == Selection::Extend) {
665 list<Selectable*> results;
666 framepos_t last_frame;
667 framepos_t first_frame;
668 bool same_track = false;
670 /* 1. find the last selected regionview in the track that was clicked in */
673 first_frame = max_framepos;
675 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
676 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
678 if ((*x)->region()->last_frame() > last_frame) {
679 last_frame = (*x)->region()->last_frame();
682 if ((*x)->region()->first_frame() < first_frame) {
683 first_frame = (*x)->region()->first_frame();
692 /* 2. figure out the boundaries for our search for new objects */
694 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
695 case Evoral::OverlapNone:
696 if (last_frame < clicked_regionview->region()->first_frame()) {
697 first_frame = last_frame;
698 last_frame = clicked_regionview->region()->last_frame();
700 last_frame = first_frame;
701 first_frame = clicked_regionview->region()->first_frame();
705 case Evoral::OverlapExternal:
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::OverlapInternal:
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::OverlapStart:
726 case Evoral::OverlapEnd:
727 /* nothing to do except add clicked region to selection, since it
728 overlaps with the existing selection in this track.
735 /* click in a track that has no regions selected, so extend vertically
736 to pick out all regions that are defined by the existing selection
741 first_frame = clicked_regionview->region()->position();
742 last_frame = clicked_regionview->region()->last_frame();
744 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
745 if ((*i)->region()->position() < first_frame) {
746 first_frame = (*i)->region()->position();
748 if ((*i)->region()->last_frame() + 1 > last_frame) {
749 last_frame = (*i)->region()->last_frame();
754 /* 2. find all the tracks we should select in */
756 set<RouteTimeAxisView*> relevant_tracks;
758 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
759 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
761 relevant_tracks.insert (r);
765 set<RouteTimeAxisView*> already_in_selection;
767 if (relevant_tracks.empty()) {
769 /* no tracks selected .. thus .. if the
770 regionview we're in isn't selected
771 (i.e. we're about to extend to it), then
772 find all tracks between the this one and
776 if (!selection->selected (clicked_regionview)) {
778 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
782 /* add this track to the ones we will search */
784 relevant_tracks.insert (rtv);
786 /* find the track closest to this one that
787 already a selected region.
790 RouteTimeAxisView* closest = 0;
791 int distance = INT_MAX;
792 int key = rtv->route()->order_key ();
794 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
796 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
798 if (artv && artv != rtv) {
800 pair<set<RouteTimeAxisView*>::iterator,bool> result;
802 result = already_in_selection.insert (artv);
805 /* newly added to already_in_selection */
807 int d = artv->route()->order_key ();
811 if (abs (d) < distance) {
821 /* now add all tracks between that one and this one */
823 int okey = closest->route()->order_key ();
829 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
830 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
831 if (artv && artv != rtv) {
833 int k = artv->route()->order_key ();
835 if (k >= okey && k <= key) {
837 /* in range but don't add it if
838 it already has tracks selected.
839 this avoids odd selection
840 behaviour that feels wrong.
843 if (find (already_in_selection.begin(),
844 already_in_selection.end(),
845 artv) == already_in_selection.end()) {
847 relevant_tracks.insert (artv);
857 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
858 one that was clicked.
861 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
862 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
865 /* 4. convert to a vector of regions */
867 vector<RegionView*> regions;
869 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
872 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
873 regions.push_back (arv);
877 if (!regions.empty()) {
878 selection->add (regions);
889 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
891 vector<RegionView*> all_equivalent_regions;
893 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
895 if (all_equivalent_regions.empty()) {
899 begin_reversible_selection_op (_("set selected regions"));
902 case Selection::Toggle:
903 /* XXX this is not correct */
904 selection->toggle (all_equivalent_regions);
907 selection->set (all_equivalent_regions);
909 case Selection::Extend:
910 selection->add (all_equivalent_regions);
913 selection->add (all_equivalent_regions);
917 commit_reversible_selection_op () ;
921 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
924 boost::shared_ptr<Region> r (weak_r.lock());
930 if ((rv = sv->find_view (r)) == 0) {
934 /* don't reset the selection if its something other than
935 a single other region.
938 if (selection->regions.size() > 1) {
942 begin_reversible_selection_op (_("set selected regions"));
946 commit_reversible_selection_op () ;
952 Editor::track_selection_changed ()
954 switch (selection->tracks.size()) {
958 set_selected_mixer_strip (*(selection->tracks.front()));
962 RouteNotificationListPtr routes (new RouteNotificationList);
964 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
966 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
968 (*i)->set_selected (yn);
970 TimeAxisView::Children c = (*i)->get_child_list ();
971 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
972 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
976 (*i)->reshow_selection (selection->time);
978 (*i)->hide_selection ();
983 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
985 routes->push_back (rtav->route());
990 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
992 /* notify control protocols */
994 ControlProtocol::TrackSelectionChanged (routes);
998 Editor::time_selection_changed ()
1000 if (Profile->get_sae()) {
1004 /* XXX this is superficially inefficient. Hide the selection in all
1005 * tracks, then show it in all selected tracks.
1007 * However, if you investigate what this actually does, it isn't
1008 * anywhere nearly as bad as it may appear. Remember: nothing is
1009 * redrawn or even recomputed during these two loops - that only
1010 * happens when we next render ...
1013 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1014 (*i)->hide_selection ();
1017 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1018 (*i)->show_selection (selection->time);
1021 if (selection->time.empty()) {
1022 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1024 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1028 /** Set all region actions to have a given sensitivity */
1030 Editor::sensitize_all_region_actions (bool s)
1032 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1034 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1035 (*i)->set_sensitive (s);
1038 _all_region_actions_sensitized = s;
1041 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1042 * This method should be called just before displaying a Region menu. When a Region menu is not
1043 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1044 * on entered_regionviews work without having to check sensitivity every time the selection or
1045 * entered_regionview changes.
1047 * This method also sets up toggle action state as appropriate.
1050 Editor::sensitize_the_right_region_actions ()
1053 RegionSelection rs = get_regions_from_selection_and_entered ();
1054 sensitize_all_region_actions (!rs.empty ());
1056 _ignore_region_action = true;
1058 /* Look through the regions that are selected and make notes about what we have got */
1060 bool have_audio = false;
1061 bool have_multichannel_audio = false;
1062 bool have_midi = false;
1063 bool have_locked = false;
1064 bool have_unlocked = false;
1065 bool have_video_locked = false;
1066 bool have_video_unlocked = false;
1067 bool have_position_lock_style_audio = false;
1068 bool have_position_lock_style_music = false;
1069 bool have_muted = false;
1070 bool have_unmuted = false;
1071 bool have_opaque = false;
1072 bool have_non_opaque = false;
1073 bool have_not_at_natural_position = false;
1074 bool have_envelope_active = false;
1075 bool have_envelope_inactive = false;
1076 bool have_non_unity_scale_amplitude = false;
1077 bool have_compound_regions = false;
1078 bool have_inactive_fade_in = false;
1079 bool have_inactive_fade_out = false;
1080 bool have_active_fade_in = false;
1081 bool have_active_fade_out = false;
1083 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1085 boost::shared_ptr<Region> r = (*i)->region ();
1086 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1090 if (ar->n_channels() > 1) {
1091 have_multichannel_audio = true;
1095 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1099 if (r->is_compound()) {
1100 have_compound_regions = true;
1106 have_unlocked = true;
1109 if (r->video_locked()) {
1110 have_video_locked = true;
1112 have_video_unlocked = true;
1115 if (r->position_lock_style() == MusicTime) {
1116 have_position_lock_style_music = true;
1118 have_position_lock_style_audio = true;
1124 have_unmuted = true;
1130 have_non_opaque = true;
1133 if (!r->at_natural_position()) {
1134 have_not_at_natural_position = true;
1138 if (ar->envelope_active()) {
1139 have_envelope_active = true;
1141 have_envelope_inactive = true;
1144 if (ar->scale_amplitude() != 1) {
1145 have_non_unity_scale_amplitude = true;
1148 if (ar->fade_in_active ()) {
1149 have_active_fade_in = true;
1151 have_inactive_fade_in = true;
1154 if (ar->fade_out_active ()) {
1155 have_active_fade_out = true;
1157 have_inactive_fade_out = true;
1162 if (rs.size() > 1) {
1163 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1164 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1165 _region_actions->get_action("rename-region")->set_sensitive (false);
1167 /* XXX need to check whether there is than 1 per
1168 playlist, because otherwise this makes no sense.
1170 _region_actions->get_action("combine-regions")->set_sensitive (true);
1172 _region_actions->get_action("combine-regions")->set_sensitive (false);
1174 } else if (rs.size() == 1) {
1175 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1176 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1177 _region_actions->get_action("combine-regions")->set_sensitive (false);
1180 if (!have_multichannel_audio) {
1181 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1185 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1186 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1187 _region_actions->get_action("quantize-region")->set_sensitive (false);
1188 _region_actions->get_action("legatize-region")->set_sensitive (false);
1189 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1190 _region_actions->get_action("fork-region")->set_sensitive (false);
1191 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1192 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1193 _region_actions->get_action("transpose-region")->set_sensitive (false);
1195 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1196 /* others were already marked sensitive */
1199 if (_edit_point == EditAtMouse) {
1200 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1201 _region_actions->get_action("trim-front")->set_sensitive (false);
1202 _region_actions->get_action("trim-back")->set_sensitive (false);
1203 _region_actions->get_action("split-region")->set_sensitive (false);
1204 _region_actions->get_action("place-transient")->set_sensitive (false);
1207 if (have_compound_regions) {
1208 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1210 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1215 if (have_envelope_active && !have_envelope_inactive) {
1216 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1217 } else if (have_envelope_active && have_envelope_inactive) {
1218 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1223 _region_actions->get_action("analyze-region")->set_sensitive (false);
1224 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1225 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1226 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1230 if (!have_non_unity_scale_amplitude || !have_audio) {
1231 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1234 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1235 a->set_active (have_locked && !have_unlocked);
1236 if (have_locked && have_unlocked) {
1237 // a->set_inconsistent ();
1240 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1241 a->set_active (have_video_locked && !have_video_unlocked);
1242 if (have_video_locked && have_video_unlocked) {
1243 // a->set_inconsistent ();
1246 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1247 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1249 if (have_position_lock_style_music && have_position_lock_style_audio) {
1250 // a->set_inconsistent ();
1253 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1254 a->set_active (have_muted && !have_unmuted);
1255 if (have_muted && have_unmuted) {
1256 // a->set_inconsistent ();
1259 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1260 a->set_active (have_opaque && !have_non_opaque);
1261 if (have_opaque && have_non_opaque) {
1262 // a->set_inconsistent ();
1265 if (!have_not_at_natural_position) {
1266 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1269 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1270 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1271 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1273 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1276 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1277 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1278 if (have_active_fade_in && have_inactive_fade_in) {
1279 // a->set_inconsistent ();
1282 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1283 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1285 if (have_active_fade_out && have_inactive_fade_out) {
1286 // a->set_inconsistent ();
1289 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1290 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1292 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1293 a->set_active (have_active_fade && !have_inactive_fade);
1295 if (have_active_fade && have_inactive_fade) {
1296 // a->set_inconsistent ();
1299 _ignore_region_action = false;
1301 _all_region_actions_sensitized = false;
1306 Editor::region_selection_changed ()
1308 _regions->block_change_connection (true);
1309 editor_regions_selection_changed_connection.block(true);
1311 if (_region_selection_change_updates_region_list) {
1312 _regions->unselect_all ();
1315 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1316 (*i)->set_selected_regionviews (selection->regions);
1319 if (_region_selection_change_updates_region_list) {
1320 _regions->set_selected (selection->regions);
1323 _regions->block_change_connection (false);
1324 editor_regions_selection_changed_connection.block(false);
1326 if (!_all_region_actions_sensitized) {
1327 /* This selection change might have changed what region actions
1328 are allowed, so sensitize them all in case a key is pressed.
1330 sensitize_all_region_actions (true);
1333 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1334 maybe_locate_with_edit_preroll (selection->regions.start());
1339 Editor::point_selection_changed ()
1341 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1342 (*i)->set_selected_points (selection->points);
1347 Editor::select_all_in_track (Selection::Operation op)
1349 list<Selectable *> touched;
1351 if (!clicked_routeview) {
1355 begin_reversible_selection_op(_("Select All in Track"));
1357 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1360 case Selection::Toggle:
1361 selection->add (touched);
1363 case Selection::Set:
1364 selection->set (touched);
1366 case Selection::Extend:
1367 /* meaningless, because we're selecting everything */
1369 case Selection::Add:
1370 selection->add (touched);
1374 commit_reversible_selection_op ();
1378 Editor::select_all_internal_edit (Selection::Operation)
1380 bool selected = false;
1382 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1383 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1385 mrv->select_all_notes ();
1390 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1392 mrv->select_all_notes ();
1400 Editor::select_all_objects (Selection::Operation op)
1402 list<Selectable *> touched;
1404 TrackViewList ts = track_views;
1406 if (internal_editing() && select_all_internal_edit(op)) {
1407 return; // Selected notes
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_selection_op (_("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_selection_op ();
1438 Editor::invert_selection_in_track ()
1440 list<Selectable *> touched;
1442 if (!clicked_routeview) {
1446 begin_reversible_selection_op(_("Invert Selection in Track"));
1447 clicked_routeview->get_inverted_selectables (*selection, touched);
1448 selection->set (touched);
1449 commit_reversible_selection_op ();
1453 Editor::invert_selection ()
1455 list<Selectable *> touched;
1457 if (internal_editing()) {
1458 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1459 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1461 mrv->invert_selection ();
1467 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1468 if ((*iter)->hidden()) {
1471 (*iter)->get_inverted_selectables (*selection, touched);
1474 begin_reversible_selection_op(_("Invert Selection"));
1475 selection->set (touched);
1476 commit_reversible_selection_op ();
1479 /** @param start Start time in session frames.
1480 * @param end End time in session frames.
1481 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1482 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1483 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1484 * within the region are already selected.
1487 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1489 list<Selectable*> found;
1491 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1493 if ((*iter)->hidden()) {
1497 (*iter)->get_selectables (start, end, top, bot, found);
1500 if (found.empty()) {
1501 selection->clear_objects();
1502 selection->clear_time ();
1506 if (preserve_if_selected && op != Selection::Toggle) {
1507 list<Selectable*>::iterator i = found.begin();
1508 while (i != found.end() && (*i)->get_selected()) {
1512 if (i == found.end()) {
1517 begin_reversible_selection_op (_("select all within"));
1519 case Selection::Add:
1520 selection->add (found);
1522 case Selection::Toggle:
1523 selection->toggle (found);
1525 case Selection::Set:
1526 selection->set (found);
1528 case Selection::Extend:
1529 /* not defined yet */
1533 commit_reversible_selection_op ();
1537 Editor::set_selection_from_region ()
1539 if (selection->regions.empty()) {
1543 selection->set (selection->regions.start(), selection->regions.end_frame());
1544 if (!Profile->get_sae()) {
1545 set_mouse_mode (Editing::MouseRange, false);
1550 Editor::set_selection_from_punch()
1554 if ((location = _session->locations()->auto_punch_location()) == 0) {
1558 set_selection_from_range (*location);
1562 Editor::set_selection_from_loop()
1566 if ((location = _session->locations()->auto_loop_location()) == 0) {
1569 set_selection_from_range (*location);
1573 Editor::set_selection_from_range (Location& loc)
1575 begin_reversible_selection_op (_("set selection from range"));
1576 selection->set (loc.start(), loc.end());
1577 commit_reversible_selection_op ();
1579 if (!Profile->get_sae()) {
1580 set_mouse_mode (Editing::MouseRange, false);
1585 Editor::select_all_selectables_using_time_selection ()
1587 list<Selectable *> touched;
1589 if (selection->time.empty()) {
1593 framepos_t start = selection->time[clicked_selection].start;
1594 framepos_t end = selection->time[clicked_selection].end;
1596 if (end - start < 1) {
1602 if (selection->tracks.empty()) {
1605 ts = &selection->tracks;
1608 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1609 if ((*iter)->hidden()) {
1612 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1615 begin_reversible_selection_op (_("select all from range"));
1616 selection->set (touched);
1617 commit_reversible_selection_op ();
1622 Editor::select_all_selectables_using_punch()
1624 Location* location = _session->locations()->auto_punch_location();
1625 list<Selectable *> touched;
1627 if (location == 0 || (location->end() - location->start() <= 1)) {
1634 if (selection->tracks.empty()) {
1637 ts = &selection->tracks;
1640 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1641 if ((*iter)->hidden()) {
1644 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1646 begin_reversible_selection_op (_("select all from punch"));
1647 selection->set (touched);
1648 commit_reversible_selection_op ();
1653 Editor::select_all_selectables_using_loop()
1655 Location* location = _session->locations()->auto_loop_location();
1656 list<Selectable *> touched;
1658 if (location == 0 || (location->end() - location->start() <= 1)) {
1665 if (selection->tracks.empty()) {
1668 ts = &selection->tracks;
1671 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1672 if ((*iter)->hidden()) {
1675 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1677 begin_reversible_selection_op (_("select all from loop"));
1678 selection->set (touched);
1679 commit_reversible_selection_op ();
1684 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1688 list<Selectable *> touched;
1691 start = cursor->current_frame();
1692 end = _session->current_end_frame();
1694 if (cursor->current_frame() > 0) {
1696 end = cursor->current_frame() - 1;
1702 if (internal_editing()) {
1703 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1704 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1706 mrv->select_range (start, end);
1713 begin_reversible_selection_op (_("select all after cursor"));
1715 begin_reversible_selection_op (_("select all before cursor"));
1720 if (selection->tracks.empty()) {
1723 ts = &selection->tracks;
1726 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1727 if ((*iter)->hidden()) {
1730 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1732 selection->set (touched);
1733 commit_reversible_selection_op ();
1737 Editor::select_all_selectables_using_edit (bool after)
1741 list<Selectable *> touched;
1744 start = get_preferred_edit_position(false, true);
1745 end = _session->current_end_frame();
1747 if ((end = get_preferred_edit_position(false, true)) > 1) {
1755 if (internal_editing()) {
1756 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1757 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1758 mrv->select_range (start, end);
1764 begin_reversible_selection_op (_("select all after edit"));
1766 begin_reversible_selection_op (_("select all before edit"));
1771 if (selection->tracks.empty()) {
1774 ts = &selection->tracks;
1777 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1778 if ((*iter)->hidden()) {
1781 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1783 selection->set (touched);
1784 commit_reversible_selection_op ();
1788 Editor::select_all_selectables_between (bool /*within*/)
1792 list<Selectable *> touched;
1794 if (!get_edit_op_range (start, end)) {
1798 if (internal_editing()) {
1799 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1800 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1801 mrv->select_range (start, end);
1808 if (selection->tracks.empty()) {
1811 ts = &selection->tracks;
1814 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1815 if ((*iter)->hidden()) {
1818 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1821 begin_reversible_selection_op(_("Select all Selectables Between"));
1822 selection->set (touched);
1823 commit_reversible_selection_op ();
1827 Editor::select_range_between ()
1832 if ( !selection->time.empty() ) {
1833 selection->clear_time ();
1836 if (!get_edit_op_range (start, end)) {
1840 begin_reversible_selection_op(_("Select Range Between"));
1841 set_mouse_mode (MouseRange);
1842 selection->set (start, end);
1843 commit_reversible_selection_op ();
1847 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1852 /* if an explicit range exists, use it */
1854 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1855 /* we know that these are ordered */
1856 start = selection->time.start();
1857 end = selection->time.end_frame();
1865 // if (!mouse_frame (m, ignored)) {
1866 // /* mouse is not in a canvas, try playhead+selected marker.
1867 // this is probably most true when using menus.
1870 // if (selection->markers.empty()) {
1874 // start = selection->markers.front()->position();
1875 // end = _session->audible_frame();
1879 // switch (_edit_point) {
1880 // case EditAtPlayhead:
1881 // if (selection->markers.empty()) {
1882 // /* use mouse + playhead */
1884 // end = _session->audible_frame();
1886 // /* use playhead + selected marker */
1887 // start = _session->audible_frame();
1888 // end = selection->markers.front()->position();
1892 // case EditAtMouse:
1893 // /* use mouse + selected marker */
1894 // if (selection->markers.empty()) {
1896 // end = _session->audible_frame();
1898 // start = selection->markers.front()->position();
1903 // case EditAtSelectedMarker:
1904 // /* use mouse + selected marker */
1905 // if (selection->markers.empty()) {
1907 // MessageDialog win (_("No edit range defined"),
1912 // win.set_secondary_text (
1913 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1916 // win.set_default_response (RESPONSE_CLOSE);
1917 // win.set_position (Gtk::WIN_POS_MOUSE);
1922 // return false; // NO RANGE
1924 // start = selection->markers.front()->position();
1930 // if (start == end) {
1934 // if (start > end) {
1935 // swap (start, end);
1938 /* turn range into one delimited by start...end,
1948 Editor::deselect_all ()
1950 begin_reversible_selection_op(_("Clear Selection"));
1951 selection->clear ();
1952 commit_reversible_selection_op ();
1956 Editor::select_range (framepos_t s, framepos_t e)
1958 begin_reversible_selection_op(_("Select Range"));
1959 selection->add (clicked_axisview);
1960 selection->time.clear ();
1961 long ret = selection->set (s, e);
1962 commit_reversible_selection_op ();