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;
1381 TrackViewList ts = track_views;
1383 if (_internal_editing) {
1385 bool midi_selected = false;
1387 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1388 if ((*iter)->hidden()) {
1392 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*iter);
1394 if (rtav && rtav->is_midi_track()) {
1395 midi_selected = true;
1400 if (midi_selected) {
1401 select_all_internal_edit (op);
1406 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1407 if ((*iter)->hidden()) {
1410 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1411 selection->add (*iter);
1415 begin_reversible_command (_("select all"));
1417 case Selection::Add:
1418 selection->add (touched);
1420 case Selection::Toggle:
1421 selection->add (touched);
1423 case Selection::Set:
1424 selection->set (touched);
1426 case Selection::Extend:
1427 /* meaningless, because we're selecting everything */
1430 commit_reversible_command ();
1434 Editor::invert_selection_in_track ()
1436 list<Selectable *> touched;
1438 if (!clicked_routeview) {
1442 clicked_routeview->get_inverted_selectables (*selection, touched);
1443 selection->set (touched);
1447 Editor::invert_selection ()
1449 list<Selectable *> touched;
1451 if (_internal_editing) {
1452 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1453 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1455 mrv->invert_selection ();
1461 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1462 if ((*iter)->hidden()) {
1465 (*iter)->get_inverted_selectables (*selection, touched);
1468 selection->set (touched);
1471 /** @param start Start time in session frames.
1472 * @param end End time in session frames.
1473 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1474 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1475 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1476 * within the region are already selected.
1479 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1481 list<Selectable*> found;
1483 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1485 if ((*iter)->hidden()) {
1489 (*iter)->get_selectables (start, end, top, bot, found);
1492 if (found.empty()) {
1493 selection->clear_objects();
1494 selection->clear_time ();
1498 if (preserve_if_selected && op != Selection::Toggle) {
1499 list<Selectable*>::iterator i = found.begin();
1500 while (i != found.end() && (*i)->get_selected()) {
1504 if (i == found.end()) {
1509 begin_reversible_command (_("select all within"));
1511 case Selection::Add:
1512 selection->add (found);
1514 case Selection::Toggle:
1515 selection->toggle (found);
1517 case Selection::Set:
1518 selection->set (found);
1520 case Selection::Extend:
1521 /* not defined yet */
1525 commit_reversible_command ();
1529 Editor::set_selection_from_region ()
1531 if (selection->regions.empty()) {
1535 selection->set (selection->regions.start(), selection->regions.end_frame());
1536 if (!Profile->get_sae()) {
1537 set_mouse_mode (Editing::MouseRange, false);
1542 Editor::set_selection_from_punch()
1546 if ((location = _session->locations()->auto_punch_location()) == 0) {
1550 set_selection_from_range (*location);
1554 Editor::set_selection_from_loop()
1558 if ((location = _session->locations()->auto_loop_location()) == 0) {
1561 set_selection_from_range (*location);
1565 Editor::set_selection_from_range (Location& loc)
1567 begin_reversible_command (_("set selection from range"));
1568 selection->set (loc.start(), loc.end());
1569 commit_reversible_command ();
1571 if (!Profile->get_sae()) {
1572 set_mouse_mode (Editing::MouseRange, false);
1577 Editor::select_all_selectables_using_time_selection ()
1579 list<Selectable *> touched;
1581 if (selection->time.empty()) {
1585 framepos_t start = selection->time[clicked_selection].start;
1586 framepos_t end = selection->time[clicked_selection].end;
1588 if (end - start < 1) {
1594 if (selection->tracks.empty()) {
1597 ts = &selection->tracks;
1600 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1601 if ((*iter)->hidden()) {
1604 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1607 begin_reversible_command (_("select all from range"));
1608 selection->set (touched);
1609 commit_reversible_command ();
1614 Editor::select_all_selectables_using_punch()
1616 Location* location = _session->locations()->auto_punch_location();
1617 list<Selectable *> touched;
1619 if (location == 0 || (location->end() - location->start() <= 1)) {
1626 if (selection->tracks.empty()) {
1629 ts = &selection->tracks;
1632 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1633 if ((*iter)->hidden()) {
1636 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1638 begin_reversible_command (_("select all from punch"));
1639 selection->set (touched);
1640 commit_reversible_command ();
1645 Editor::select_all_selectables_using_loop()
1647 Location* location = _session->locations()->auto_loop_location();
1648 list<Selectable *> touched;
1650 if (location == 0 || (location->end() - location->start() <= 1)) {
1657 if (selection->tracks.empty()) {
1660 ts = &selection->tracks;
1663 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1664 if ((*iter)->hidden()) {
1667 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1669 begin_reversible_command (_("select all from loop"));
1670 selection->set (touched);
1671 commit_reversible_command ();
1676 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1680 list<Selectable *> touched;
1683 start = cursor->current_frame();
1684 end = _session->current_end_frame();
1686 if (cursor->current_frame() > 0) {
1688 end = cursor->current_frame() - 1;
1694 if (_internal_editing) {
1695 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1696 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1698 mrv->select_range (start, end);
1705 begin_reversible_command (_("select all after cursor"));
1707 begin_reversible_command (_("select all before cursor"));
1712 if (selection->tracks.empty()) {
1715 ts = &selection->tracks;
1718 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1719 if ((*iter)->hidden()) {
1722 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1724 selection->set (touched);
1725 commit_reversible_command ();
1729 Editor::select_all_selectables_using_edit (bool after)
1733 list<Selectable *> touched;
1736 start = get_preferred_edit_position();
1737 end = _session->current_end_frame();
1739 if ((end = get_preferred_edit_position()) > 1) {
1747 if (_internal_editing) {
1748 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1749 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1750 mrv->select_range (start, end);
1756 begin_reversible_command (_("select all after edit"));
1758 begin_reversible_command (_("select all before edit"));
1763 if (selection->tracks.empty()) {
1766 ts = &selection->tracks;
1769 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1770 if ((*iter)->hidden()) {
1773 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1775 selection->set (touched);
1776 commit_reversible_command ();
1780 Editor::select_all_selectables_between (bool /*within*/)
1784 list<Selectable *> touched;
1786 if (!get_edit_op_range (start, end)) {
1790 if (_internal_editing) {
1791 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1792 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1793 mrv->select_range (start, end);
1800 if (selection->tracks.empty()) {
1803 ts = &selection->tracks;
1806 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1807 if ((*iter)->hidden()) {
1810 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1813 selection->set (touched);
1817 Editor::select_range_between ()
1822 if ( !selection->time.empty() ) {
1823 selection->clear_time ();
1826 if (!get_edit_op_range (start, end)) {
1830 set_mouse_mode (MouseRange);
1831 selection->set (start, end);
1835 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1840 /* if an explicit range exists, use it */
1842 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1843 /* we know that these are ordered */
1844 start = selection->time.start();
1845 end = selection->time.end_frame();
1853 // if (!mouse_frame (m, ignored)) {
1854 // /* mouse is not in a canvas, try playhead+selected marker.
1855 // this is probably most true when using menus.
1858 // if (selection->markers.empty()) {
1862 // start = selection->markers.front()->position();
1863 // end = _session->audible_frame();
1867 // switch (_edit_point) {
1868 // case EditAtPlayhead:
1869 // if (selection->markers.empty()) {
1870 // /* use mouse + playhead */
1872 // end = _session->audible_frame();
1874 // /* use playhead + selected marker */
1875 // start = _session->audible_frame();
1876 // end = selection->markers.front()->position();
1880 // case EditAtMouse:
1881 // /* use mouse + selected marker */
1882 // if (selection->markers.empty()) {
1884 // end = _session->audible_frame();
1886 // start = selection->markers.front()->position();
1891 // case EditAtSelectedMarker:
1892 // /* use mouse + selected marker */
1893 // if (selection->markers.empty()) {
1895 // MessageDialog win (_("No edit range defined"),
1900 // win.set_secondary_text (
1901 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1904 // win.set_default_response (RESPONSE_CLOSE);
1905 // win.set_position (Gtk::WIN_POS_MOUSE);
1910 // return false; // NO RANGE
1912 // start = selection->markers.front()->position();
1918 // if (start == end) {
1922 // if (start > end) {
1923 // swap (start, end);
1926 /* turn range into one delimited by start...end,
1936 Editor::deselect_all ()
1938 selection->clear ();
1942 Editor::select_range (framepos_t s, framepos_t e)
1944 selection->add (clicked_axisview);
1945 selection->time.clear ();
1946 return selection->set (s, e);