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 if (!clicked_routeview) {
192 bool had_tracks = !selection->tracks.empty();
193 RouteGroup* group = clicked_routeview->route()->route_group();
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 /* no commit necessary: clicked on an already selected region */
653 } else if (op == Selection::Extend) {
655 list<Selectable*> results;
656 framepos_t last_frame;
657 framepos_t first_frame;
658 bool same_track = false;
660 /* 1. find the last selected regionview in the track that was clicked in */
663 first_frame = max_framepos;
665 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
666 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
668 if ((*x)->region()->last_frame() > last_frame) {
669 last_frame = (*x)->region()->last_frame();
672 if ((*x)->region()->first_frame() < first_frame) {
673 first_frame = (*x)->region()->first_frame();
682 /* 2. figure out the boundaries for our search for new objects */
684 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
685 case Evoral::OverlapNone:
686 if (last_frame < clicked_regionview->region()->first_frame()) {
687 first_frame = last_frame;
688 last_frame = clicked_regionview->region()->last_frame();
690 last_frame = first_frame;
691 first_frame = clicked_regionview->region()->first_frame();
695 case Evoral::OverlapExternal:
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::OverlapInternal:
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::OverlapStart:
716 case Evoral::OverlapEnd:
717 /* nothing to do except add clicked region to selection, since it
718 overlaps with the existing selection in this track.
725 /* click in a track that has no regions selected, so extend vertically
726 to pick out all regions that are defined by the existing selection
731 first_frame = clicked_regionview->region()->position();
732 last_frame = clicked_regionview->region()->last_frame();
734 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
735 if ((*i)->region()->position() < first_frame) {
736 first_frame = (*i)->region()->position();
738 if ((*i)->region()->last_frame() + 1 > last_frame) {
739 last_frame = (*i)->region()->last_frame();
744 /* 2. find all the tracks we should select in */
746 set<RouteTimeAxisView*> relevant_tracks;
748 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
749 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
751 relevant_tracks.insert (r);
755 set<RouteTimeAxisView*> already_in_selection;
757 if (relevant_tracks.empty()) {
759 /* no tracks selected .. thus .. if the
760 regionview we're in isn't selected
761 (i.e. we're about to extend to it), then
762 find all tracks between the this one and
766 if (!selection->selected (clicked_regionview)) {
768 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
772 /* add this track to the ones we will search */
774 relevant_tracks.insert (rtv);
776 /* find the track closest to this one that
777 already a selected region.
780 RouteTimeAxisView* closest = 0;
781 int distance = INT_MAX;
782 int key = rtv->route()->order_key ();
784 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
786 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
788 if (artv && artv != rtv) {
790 pair<set<RouteTimeAxisView*>::iterator,bool> result;
792 result = already_in_selection.insert (artv);
795 /* newly added to already_in_selection */
797 int d = artv->route()->order_key ();
801 if (abs (d) < distance) {
811 /* now add all tracks between that one and this one */
813 int okey = closest->route()->order_key ();
819 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
820 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
821 if (artv && artv != rtv) {
823 int k = artv->route()->order_key ();
825 if (k >= okey && k <= key) {
827 /* in range but don't add it if
828 it already has tracks selected.
829 this avoids odd selection
830 behaviour that feels wrong.
833 if (find (already_in_selection.begin(),
834 already_in_selection.end(),
835 artv) == already_in_selection.end()) {
837 relevant_tracks.insert (artv);
847 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
848 one that was clicked.
851 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
852 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
855 /* 4. convert to a vector of regions */
857 vector<RegionView*> regions;
859 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
862 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
863 regions.push_back (arv);
867 if (!regions.empty()) {
868 selection->add (regions);
879 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
881 vector<RegionView*> all_equivalent_regions;
883 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
885 if (all_equivalent_regions.empty()) {
889 begin_reversible_command (_("set selected regions"));
892 case Selection::Toggle:
893 /* XXX this is not correct */
894 selection->toggle (all_equivalent_regions);
897 selection->set (all_equivalent_regions);
899 case Selection::Extend:
900 selection->add (all_equivalent_regions);
903 selection->add (all_equivalent_regions);
907 commit_reversible_command () ;
911 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
914 boost::shared_ptr<Region> r (weak_r.lock());
920 if ((rv = sv->find_view (r)) == 0) {
924 /* don't reset the selection if its something other than
925 a single other region.
928 if (selection->regions.size() > 1) {
932 begin_reversible_command (_("set selected regions"));
936 commit_reversible_command () ;
942 Editor::track_selection_changed ()
944 switch (selection->tracks.size()) {
948 set_selected_mixer_strip (*(selection->tracks.front()));
952 RouteNotificationListPtr routes (new RouteNotificationList);
954 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
956 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
958 (*i)->set_selected (yn);
960 TimeAxisView::Children c = (*i)->get_child_list ();
961 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
962 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
966 (*i)->reshow_selection (selection->time);
968 (*i)->hide_selection ();
973 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
975 routes->push_back (rtav->route());
980 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
982 /* notify control protocols */
984 ControlProtocol::TrackSelectionChanged (routes);
988 Editor::time_selection_changed ()
990 if (Profile->get_sae()) {
994 /* XXX this is superficially inefficient. Hide the selection in all
995 * tracks, then show it in all selected tracks.
997 * However, if you investigate what this actually does, it isn't
998 * anywhere nearly as bad as it may appear. Remember: nothing is
999 * redrawn or even recomputed during these two loops - that only
1000 * happens when we next render ...
1003 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1004 (*i)->hide_selection ();
1007 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1008 (*i)->show_selection (selection->time);
1011 if (selection->time.empty()) {
1012 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1014 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1017 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
1018 _session->request_locate (selection->time.start());
1022 /** Set all region actions to have a given sensitivity */
1024 Editor::sensitize_all_region_actions (bool s)
1026 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1028 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1029 (*i)->set_sensitive (s);
1032 _all_region_actions_sensitized = s;
1035 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1036 * This method should be called just before displaying a Region menu. When a Region menu is not
1037 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1038 * on entered_regionviews work without having to check sensitivity every time the selection or
1039 * entered_regionview changes.
1041 * This method also sets up toggle action state as appropriate.
1044 Editor::sensitize_the_right_region_actions ()
1047 RegionSelection rs = get_regions_from_selection_and_entered ();
1048 sensitize_all_region_actions (!rs.empty ());
1050 _ignore_region_action = true;
1052 /* Look through the regions that are selected and make notes about what we have got */
1054 bool have_audio = false;
1055 bool have_multichannel_audio = false;
1056 bool have_midi = false;
1057 bool have_locked = false;
1058 bool have_unlocked = false;
1059 bool have_video_locked = false;
1060 bool have_video_unlocked = false;
1061 bool have_position_lock_style_audio = false;
1062 bool have_position_lock_style_music = false;
1063 bool have_muted = false;
1064 bool have_unmuted = false;
1065 bool have_opaque = false;
1066 bool have_non_opaque = false;
1067 bool have_not_at_natural_position = false;
1068 bool have_envelope_active = false;
1069 bool have_envelope_inactive = false;
1070 bool have_non_unity_scale_amplitude = false;
1071 bool have_compound_regions = false;
1072 bool have_inactive_fade_in = false;
1073 bool have_inactive_fade_out = false;
1074 bool have_active_fade_in = false;
1075 bool have_active_fade_out = false;
1077 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1079 boost::shared_ptr<Region> r = (*i)->region ();
1080 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1084 if (ar->n_channels() > 1) {
1085 have_multichannel_audio = true;
1089 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1093 if (r->is_compound()) {
1094 have_compound_regions = true;
1100 have_unlocked = true;
1103 if (r->video_locked()) {
1104 have_video_locked = true;
1106 have_video_unlocked = true;
1109 if (r->position_lock_style() == MusicTime) {
1110 have_position_lock_style_music = true;
1112 have_position_lock_style_audio = true;
1118 have_unmuted = true;
1124 have_non_opaque = true;
1127 if (!r->at_natural_position()) {
1128 have_not_at_natural_position = true;
1132 if (ar->envelope_active()) {
1133 have_envelope_active = true;
1135 have_envelope_inactive = true;
1138 if (ar->scale_amplitude() != 1) {
1139 have_non_unity_scale_amplitude = true;
1142 if (ar->fade_in_active ()) {
1143 have_active_fade_in = true;
1145 have_inactive_fade_in = true;
1148 if (ar->fade_out_active ()) {
1149 have_active_fade_out = true;
1151 have_inactive_fade_out = true;
1156 if (rs.size() > 1) {
1157 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1158 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1159 _region_actions->get_action("rename-region")->set_sensitive (false);
1161 /* XXX need to check whether there is than 1 per
1162 playlist, because otherwise this makes no sense.
1164 _region_actions->get_action("combine-regions")->set_sensitive (true);
1166 _region_actions->get_action("combine-regions")->set_sensitive (false);
1168 } else if (rs.size() == 1) {
1169 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1170 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1171 _region_actions->get_action("combine-regions")->set_sensitive (false);
1174 if (!have_multichannel_audio) {
1175 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1179 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1180 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1181 _region_actions->get_action("quantize-region")->set_sensitive (false);
1182 _region_actions->get_action("fork-region")->set_sensitive (false);
1183 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1184 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1185 _region_actions->get_action("transpose-region")->set_sensitive (false);
1187 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1188 /* others were already marked sensitive */
1191 if (_edit_point == EditAtMouse) {
1192 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1193 _region_actions->get_action("trim-front")->set_sensitive (false);
1194 _region_actions->get_action("trim-back")->set_sensitive (false);
1195 _region_actions->get_action("split-region")->set_sensitive (false);
1196 _region_actions->get_action("place-transient")->set_sensitive (false);
1199 if (have_compound_regions) {
1200 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1202 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1207 if (have_envelope_active && !have_envelope_inactive) {
1208 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1209 } else if (have_envelope_active && have_envelope_inactive) {
1210 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1215 _region_actions->get_action("analyze-region")->set_sensitive (false);
1216 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1217 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1218 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1222 if (!have_non_unity_scale_amplitude || !have_audio) {
1223 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1226 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1227 a->set_active (have_locked && !have_unlocked);
1228 if (have_locked && have_unlocked) {
1229 // a->set_inconsistent ();
1232 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1233 a->set_active (have_video_locked && !have_video_unlocked);
1234 if (have_video_locked && have_video_unlocked) {
1235 // a->set_inconsistent ();
1238 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1239 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1241 if (have_position_lock_style_music && have_position_lock_style_audio) {
1242 // a->set_inconsistent ();
1245 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1246 a->set_active (have_muted && !have_unmuted);
1247 if (have_muted && have_unmuted) {
1248 // a->set_inconsistent ();
1251 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1252 a->set_active (have_opaque && !have_non_opaque);
1253 if (have_opaque && have_non_opaque) {
1254 // a->set_inconsistent ();
1257 if (!have_not_at_natural_position) {
1258 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1261 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1262 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1263 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1265 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1268 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1269 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1270 if (have_active_fade_in && have_inactive_fade_in) {
1271 // a->set_inconsistent ();
1274 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1275 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1277 if (have_active_fade_out && have_inactive_fade_out) {
1278 // a->set_inconsistent ();
1281 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1282 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1284 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1285 a->set_active (have_active_fade && !have_inactive_fade);
1287 if (have_active_fade && have_inactive_fade) {
1288 // a->set_inconsistent ();
1291 _ignore_region_action = false;
1293 _all_region_actions_sensitized = false;
1298 Editor::region_selection_changed ()
1300 _regions->block_change_connection (true);
1301 editor_regions_selection_changed_connection.block(true);
1303 if (_region_selection_change_updates_region_list) {
1304 _regions->unselect_all ();
1307 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1308 (*i)->set_selected_regionviews (selection->regions);
1311 if (_region_selection_change_updates_region_list) {
1312 _regions->set_selected (selection->regions);
1315 _regions->block_change_connection (false);
1316 editor_regions_selection_changed_connection.block(false);
1318 if (!_all_region_actions_sensitized) {
1319 /* This selection change might have changed what region actions
1320 are allowed, so sensitize them all in case a key is pressed.
1322 sensitize_all_region_actions (true);
1325 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1326 maybe_locate_with_edit_preroll (selection->regions.start());
1331 Editor::point_selection_changed ()
1333 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1334 (*i)->set_selected_points (selection->points);
1339 Editor::select_all_in_track (Selection::Operation op)
1341 list<Selectable *> touched;
1343 if (!clicked_routeview) {
1347 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1350 case Selection::Toggle:
1351 selection->add (touched);
1353 case Selection::Set:
1354 selection->set (touched);
1356 case Selection::Extend:
1357 /* meaningless, because we're selecting everything */
1359 case Selection::Add:
1360 selection->add (touched);
1366 Editor::select_all_internal_edit (Selection::Operation)
1368 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1369 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1371 mrv->select_all_notes ();
1377 Editor::select_all (Selection::Operation op)
1379 list<Selectable *> touched;
1383 if (selection->tracks.empty()) {
1384 if (entered_track) {
1385 ts.push_back (entered_track);
1390 ts = selection->tracks;
1393 if (_internal_editing) {
1395 bool midi_selected = false;
1397 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1398 if ((*iter)->hidden()) {
1402 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*iter);
1404 if (rtav && rtav->is_midi_track()) {
1405 midi_selected = true;
1410 if (midi_selected) {
1411 select_all_internal_edit (op);
1416 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1417 if ((*iter)->hidden()) {
1420 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1423 begin_reversible_command (_("select all"));
1425 case Selection::Add:
1426 selection->add (touched);
1428 case Selection::Toggle:
1429 selection->add (touched);
1431 case Selection::Set:
1432 selection->set (touched);
1434 case Selection::Extend:
1435 /* meaningless, because we're selecting everything */
1438 commit_reversible_command ();
1442 Editor::invert_selection_in_track ()
1444 list<Selectable *> touched;
1446 if (!clicked_routeview) {
1450 clicked_routeview->get_inverted_selectables (*selection, touched);
1451 selection->set (touched);
1455 Editor::invert_selection ()
1457 list<Selectable *> touched;
1459 if (_internal_editing) {
1460 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1461 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1463 mrv->invert_selection ();
1469 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1470 if ((*iter)->hidden()) {
1473 (*iter)->get_inverted_selectables (*selection, touched);
1476 selection->set (touched);
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_command (_("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_command ();
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_command (_("set selection from range"));
1576 selection->set (loc.start(), loc.end());
1577 commit_reversible_command ();
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_command (_("select all from range"));
1616 selection->set (touched);
1617 commit_reversible_command ();
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_command (_("select all from punch"));
1647 selection->set (touched);
1648 commit_reversible_command ();
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_command (_("select all from loop"));
1678 selection->set (touched);
1679 commit_reversible_command ();
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_command (_("select all after cursor"));
1715 begin_reversible_command (_("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_command ();
1737 Editor::select_all_selectables_using_edit (bool after)
1741 list<Selectable *> touched;
1744 start = get_preferred_edit_position();
1745 end = _session->current_end_frame();
1747 if ((end = get_preferred_edit_position()) > 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_command (_("select all after edit"));
1766 begin_reversible_command (_("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_command ();
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 selection->set (touched);
1825 Editor::select_range_between ()
1830 if ( !selection->time.empty() ) {
1831 selection->clear_time ();
1834 if (!get_edit_op_range (start, end)) {
1838 set_mouse_mode (MouseRange);
1839 selection->set (start, end);
1843 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1848 /* if an explicit range exists, use it */
1850 if (!selection->time.empty()) {
1851 /* we know that these are ordered */
1852 start = selection->time.start();
1853 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();
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();
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);