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 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
995 (*i)->hide_selection ();
998 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
999 (*i)->show_selection (selection->time);
1002 if (selection->time.empty()) {
1003 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1005 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1008 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
1009 _session->request_locate (selection->time.start());
1013 /** Set all region actions to have a given sensitivity */
1015 Editor::sensitize_all_region_actions (bool s)
1017 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1019 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1020 (*i)->set_sensitive (s);
1023 _all_region_actions_sensitized = s;
1026 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1027 * This method should be called just before displaying a Region menu. When a Region menu is not
1028 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1029 * on entered_regionviews work without having to check sensitivity every time the selection or
1030 * entered_regionview changes.
1032 * This method also sets up toggle action state as appropriate.
1035 Editor::sensitize_the_right_region_actions ()
1038 RegionSelection rs = get_regions_from_selection_and_entered ();
1039 sensitize_all_region_actions (!rs.empty ());
1041 _ignore_region_action = true;
1043 /* Look through the regions that are selected and make notes about what we have got */
1045 bool have_audio = false;
1046 bool have_multichannel_audio = false;
1047 bool have_midi = false;
1048 bool have_locked = false;
1049 bool have_unlocked = false;
1050 bool have_video_locked = false;
1051 bool have_video_unlocked = false;
1052 bool have_position_lock_style_audio = false;
1053 bool have_position_lock_style_music = false;
1054 bool have_muted = false;
1055 bool have_unmuted = false;
1056 bool have_opaque = false;
1057 bool have_non_opaque = false;
1058 bool have_not_at_natural_position = false;
1059 bool have_envelope_active = false;
1060 bool have_envelope_inactive = false;
1061 bool have_non_unity_scale_amplitude = false;
1062 bool have_compound_regions = false;
1063 bool have_inactive_fade_in = false;
1064 bool have_inactive_fade_out = false;
1065 bool have_active_fade_in = false;
1066 bool have_active_fade_out = false;
1068 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1070 boost::shared_ptr<Region> r = (*i)->region ();
1071 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1075 if (ar->n_channels() > 1) {
1076 have_multichannel_audio = true;
1080 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1084 if (r->is_compound()) {
1085 have_compound_regions = true;
1091 have_unlocked = true;
1094 if (r->video_locked()) {
1095 have_video_locked = true;
1097 have_video_unlocked = true;
1100 if (r->position_lock_style() == MusicTime) {
1101 have_position_lock_style_music = true;
1103 have_position_lock_style_audio = true;
1109 have_unmuted = true;
1115 have_non_opaque = true;
1118 if (!r->at_natural_position()) {
1119 have_not_at_natural_position = true;
1123 if (ar->envelope_active()) {
1124 have_envelope_active = true;
1126 have_envelope_inactive = true;
1129 if (ar->scale_amplitude() != 1) {
1130 have_non_unity_scale_amplitude = true;
1133 if (ar->fade_in_active ()) {
1134 have_active_fade_in = true;
1136 have_inactive_fade_in = true;
1139 if (ar->fade_out_active ()) {
1140 have_active_fade_out = true;
1142 have_inactive_fade_out = true;
1147 if (rs.size() > 1) {
1148 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1149 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1150 _region_actions->get_action("rename-region")->set_sensitive (false);
1152 /* XXX need to check whether there is than 1 per
1153 playlist, because otherwise this makes no sense.
1155 _region_actions->get_action("combine-regions")->set_sensitive (true);
1157 _region_actions->get_action("combine-regions")->set_sensitive (false);
1159 } else if (rs.size() == 1) {
1160 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1161 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1162 _region_actions->get_action("combine-regions")->set_sensitive (false);
1165 if (!have_multichannel_audio) {
1166 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1170 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1171 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1172 _region_actions->get_action("quantize-region")->set_sensitive (false);
1173 _region_actions->get_action("fork-region")->set_sensitive (false);
1174 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1175 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1176 _region_actions->get_action("transpose-region")->set_sensitive (false);
1178 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1179 /* others were already marked sensitive */
1182 if (_edit_point == EditAtMouse) {
1183 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1184 _region_actions->get_action("trim-front")->set_sensitive (false);
1185 _region_actions->get_action("trim-back")->set_sensitive (false);
1186 _region_actions->get_action("split-region")->set_sensitive (false);
1187 _region_actions->get_action("place-transient")->set_sensitive (false);
1190 if (have_compound_regions) {
1191 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1193 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1198 if (have_envelope_active && !have_envelope_inactive) {
1199 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1200 } else if (have_envelope_active && have_envelope_inactive) {
1201 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1206 _region_actions->get_action("analyze-region")->set_sensitive (false);
1207 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1208 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1209 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1213 if (!have_non_unity_scale_amplitude || !have_audio) {
1214 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1217 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1218 a->set_active (have_locked && !have_unlocked);
1219 if (have_locked && have_unlocked) {
1220 // a->set_inconsistent ();
1223 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1224 a->set_active (have_video_locked && !have_video_unlocked);
1225 if (have_video_locked && have_video_unlocked) {
1226 // a->set_inconsistent ();
1229 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1230 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1232 if (have_position_lock_style_music && have_position_lock_style_audio) {
1233 // a->set_inconsistent ();
1236 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1237 a->set_active (have_muted && !have_unmuted);
1238 if (have_muted && have_unmuted) {
1239 // a->set_inconsistent ();
1242 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1243 a->set_active (have_opaque && !have_non_opaque);
1244 if (have_opaque && have_non_opaque) {
1245 // a->set_inconsistent ();
1248 if (!have_not_at_natural_position) {
1249 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1252 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1253 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1254 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1256 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1259 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1260 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1261 if (have_active_fade_in && have_inactive_fade_in) {
1262 // a->set_inconsistent ();
1265 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1266 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1268 if (have_active_fade_out && have_inactive_fade_out) {
1269 // a->set_inconsistent ();
1272 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1273 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1275 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1276 a->set_active (have_active_fade && !have_inactive_fade);
1278 if (have_active_fade && have_inactive_fade) {
1279 // a->set_inconsistent ();
1282 _ignore_region_action = false;
1284 _all_region_actions_sensitized = false;
1289 Editor::region_selection_changed ()
1291 _regions->block_change_connection (true);
1292 editor_regions_selection_changed_connection.block(true);
1294 if (_region_selection_change_updates_region_list) {
1295 _regions->unselect_all ();
1298 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1299 (*i)->set_selected_regionviews (selection->regions);
1302 if (_region_selection_change_updates_region_list) {
1303 _regions->set_selected (selection->regions);
1306 _regions->block_change_connection (false);
1307 editor_regions_selection_changed_connection.block(false);
1309 if (!_all_region_actions_sensitized) {
1310 /* This selection change might have changed what region actions
1311 are allowed, so sensitize them all in case a key is pressed.
1313 sensitize_all_region_actions (true);
1316 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1317 maybe_locate_with_edit_preroll (selection->regions.start());
1322 Editor::point_selection_changed ()
1324 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1325 (*i)->set_selected_points (selection->points);
1330 Editor::select_all_in_track (Selection::Operation op)
1332 list<Selectable *> touched;
1334 if (!clicked_routeview) {
1338 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1341 case Selection::Toggle:
1342 selection->add (touched);
1344 case Selection::Set:
1345 selection->set (touched);
1347 case Selection::Extend:
1348 /* meaningless, because we're selecting everything */
1350 case Selection::Add:
1351 selection->add (touched);
1357 Editor::select_all_internal_edit (Selection::Operation)
1359 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1360 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1362 mrv->select_all_notes ();
1368 Editor::select_all (Selection::Operation op)
1370 list<Selectable *> touched;
1374 if (selection->tracks.empty()) {
1375 if (entered_track) {
1376 ts.push_back (entered_track);
1381 ts = selection->tracks;
1384 if (_internal_editing) {
1386 bool midi_selected = false;
1388 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1389 if ((*iter)->hidden()) {
1393 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*iter);
1395 if (rtav && rtav->is_midi_track()) {
1396 midi_selected = true;
1401 if (midi_selected) {
1402 select_all_internal_edit (op);
1407 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1408 if ((*iter)->hidden()) {
1411 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1414 begin_reversible_command (_("select all"));
1416 case Selection::Add:
1417 selection->add (touched);
1419 case Selection::Toggle:
1420 selection->add (touched);
1422 case Selection::Set:
1423 selection->set (touched);
1425 case Selection::Extend:
1426 /* meaningless, because we're selecting everything */
1429 commit_reversible_command ();
1433 Editor::invert_selection_in_track ()
1435 list<Selectable *> touched;
1437 if (!clicked_routeview) {
1441 clicked_routeview->get_inverted_selectables (*selection, touched);
1442 selection->set (touched);
1446 Editor::invert_selection ()
1448 list<Selectable *> touched;
1450 if (_internal_editing) {
1451 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1452 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1454 mrv->invert_selection ();
1460 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1461 if ((*iter)->hidden()) {
1464 (*iter)->get_inverted_selectables (*selection, touched);
1467 selection->set (touched);
1470 /** @param start Start time in session frames.
1471 * @param end End time in session frames.
1472 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1473 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1474 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1475 * within the region are already selected.
1478 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1480 list<Selectable*> found;
1482 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1484 if ((*iter)->hidden()) {
1488 (*iter)->get_selectables (start, end, top, bot, found);
1491 if (found.empty()) {
1492 selection->clear_objects();
1493 selection->clear_time ();
1497 if (preserve_if_selected && op != Selection::Toggle) {
1498 list<Selectable*>::iterator i = found.begin();
1499 while (i != found.end() && (*i)->get_selected()) {
1503 if (i == found.end()) {
1508 begin_reversible_command (_("select all within"));
1510 case Selection::Add:
1511 selection->add (found);
1513 case Selection::Toggle:
1514 selection->toggle (found);
1516 case Selection::Set:
1517 selection->set (found);
1519 case Selection::Extend:
1520 /* not defined yet */
1524 commit_reversible_command ();
1528 Editor::set_selection_from_region ()
1530 if (selection->regions.empty()) {
1534 selection->set (selection->regions.start(), selection->regions.end_frame());
1535 if (!Profile->get_sae()) {
1536 set_mouse_mode (Editing::MouseRange, false);
1541 Editor::set_selection_from_punch()
1545 if ((location = _session->locations()->auto_punch_location()) == 0) {
1549 set_selection_from_range (*location);
1553 Editor::set_selection_from_loop()
1557 if ((location = _session->locations()->auto_loop_location()) == 0) {
1560 set_selection_from_range (*location);
1564 Editor::set_selection_from_range (Location& loc)
1566 begin_reversible_command (_("set selection from range"));
1567 selection->set (loc.start(), loc.end());
1568 commit_reversible_command ();
1570 if (!Profile->get_sae()) {
1571 set_mouse_mode (Editing::MouseRange, false);
1576 Editor::select_all_selectables_using_time_selection ()
1578 list<Selectable *> touched;
1580 if (selection->time.empty()) {
1584 framepos_t start = selection->time[clicked_selection].start;
1585 framepos_t end = selection->time[clicked_selection].end;
1587 if (end - start < 1) {
1593 if (selection->tracks.empty()) {
1596 ts = &selection->tracks;
1599 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1600 if ((*iter)->hidden()) {
1603 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1606 begin_reversible_command (_("select all from range"));
1607 selection->set (touched);
1608 commit_reversible_command ();
1613 Editor::select_all_selectables_using_punch()
1615 Location* location = _session->locations()->auto_punch_location();
1616 list<Selectable *> touched;
1618 if (location == 0 || (location->end() - location->start() <= 1)) {
1625 if (selection->tracks.empty()) {
1628 ts = &selection->tracks;
1631 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1632 if ((*iter)->hidden()) {
1635 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1637 begin_reversible_command (_("select all from punch"));
1638 selection->set (touched);
1639 commit_reversible_command ();
1644 Editor::select_all_selectables_using_loop()
1646 Location* location = _session->locations()->auto_loop_location();
1647 list<Selectable *> touched;
1649 if (location == 0 || (location->end() - location->start() <= 1)) {
1656 if (selection->tracks.empty()) {
1659 ts = &selection->tracks;
1662 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1663 if ((*iter)->hidden()) {
1666 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1668 begin_reversible_command (_("select all from loop"));
1669 selection->set (touched);
1670 commit_reversible_command ();
1675 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1679 list<Selectable *> touched;
1682 start = cursor->current_frame();
1683 end = _session->current_end_frame();
1685 if (cursor->current_frame() > 0) {
1687 end = cursor->current_frame() - 1;
1693 if (_internal_editing) {
1694 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1695 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1697 mrv->select_range (start, end);
1704 begin_reversible_command (_("select all after cursor"));
1706 begin_reversible_command (_("select all before cursor"));
1711 if (selection->tracks.empty()) {
1714 ts = &selection->tracks;
1717 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1718 if ((*iter)->hidden()) {
1721 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1723 selection->set (touched);
1724 commit_reversible_command ();
1728 Editor::select_all_selectables_using_edit (bool after)
1732 list<Selectable *> touched;
1735 start = get_preferred_edit_position();
1736 end = _session->current_end_frame();
1738 if ((end = get_preferred_edit_position()) > 1) {
1746 if (_internal_editing) {
1747 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1748 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1749 mrv->select_range (start, end);
1755 begin_reversible_command (_("select all after edit"));
1757 begin_reversible_command (_("select all before edit"));
1762 if (selection->tracks.empty()) {
1765 ts = &selection->tracks;
1768 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1769 if ((*iter)->hidden()) {
1772 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1774 selection->set (touched);
1775 commit_reversible_command ();
1779 Editor::select_all_selectables_between (bool /*within*/)
1783 list<Selectable *> touched;
1785 if (!get_edit_op_range (start, end)) {
1789 if (_internal_editing) {
1790 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1791 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1792 mrv->select_range (start, end);
1799 if (selection->tracks.empty()) {
1802 ts = &selection->tracks;
1805 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1806 if ((*iter)->hidden()) {
1809 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1812 selection->set (touched);
1816 Editor::select_range_between ()
1821 if ( !selection->time.empty() ) {
1822 selection->clear_time ();
1825 if (!get_edit_op_range (start, end)) {
1829 set_mouse_mode (MouseRange);
1830 selection->set (start, end);
1834 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1839 /* if an explicit range exists, use it */
1841 if (!selection->time.empty()) {
1842 /* we know that these are ordered */
1843 start = selection->time.start();
1844 end = selection->time.end_frame();
1848 if (!mouse_frame (m, ignored)) {
1849 /* mouse is not in a canvas, try playhead+selected marker.
1850 this is probably most true when using menus.
1853 if (selection->markers.empty()) {
1857 start = selection->markers.front()->position();
1858 end = _session->audible_frame();
1862 switch (_edit_point) {
1863 case EditAtPlayhead:
1864 if (selection->markers.empty()) {
1865 /* use mouse + playhead */
1867 end = _session->audible_frame();
1869 /* use playhead + selected marker */
1870 start = _session->audible_frame();
1871 end = selection->markers.front()->position();
1876 /* use mouse + selected marker */
1877 if (selection->markers.empty()) {
1879 end = _session->audible_frame();
1881 start = selection->markers.front()->position();
1886 case EditAtSelectedMarker:
1887 /* use mouse + selected marker */
1888 if (selection->markers.empty()) {
1890 MessageDialog win (_("No edit range defined"),
1895 win.set_secondary_text (
1896 _("the edit point is Selected Marker\nbut there is no selected marker."));
1899 win.set_default_response (RESPONSE_CLOSE);
1900 win.set_position (Gtk::WIN_POS_MOUSE);
1905 return false; // NO RANGE
1907 start = selection->markers.front()->position();
1921 /* turn range into one delimited by start...end,
1931 Editor::deselect_all ()
1933 selection->clear ();
1937 Editor::select_range (framepos_t s, framepos_t e)
1939 selection->add (clicked_axisview);
1940 selection->time.clear ();
1941 return selection->set (s, e);