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/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29 #include "ardour/midi_region.h"
30 #include "ardour/audioplaylist.h"
32 #include "control_protocol/control_protocol.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "audio_streamview.h"
39 #include "automation_line.h"
40 #include "control_point.h"
41 #include "crossfade_view.h"
42 #include "editor_regions.h"
43 #include "editor_cursors.h"
44 #include "midi_region_view.h"
49 using namespace ARDOUR;
53 using namespace Gtkmm2ext;
54 using namespace Editing;
56 struct TrackViewByPositionSorter
58 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
59 return a->y_position() < b->y_position();
64 Editor::extend_selection_to_track (TimeAxisView& view)
66 if (selection->selected (&view)) {
67 /* already selected, do nothing */
71 if (selection->tracks.empty()) {
73 if (!selection->selected (&view)) {
74 selection->set (&view);
81 /* something is already selected, so figure out which range of things to add */
83 TrackViewList to_be_added;
84 TrackViewList sorted = track_views;
85 TrackViewByPositionSorter cmp;
86 bool passed_clicked = false;
91 if (!selection->selected (&view)) {
92 to_be_added.push_back (&view);
95 /* figure out if we should go forward or backwards */
97 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
100 passed_clicked = true;
103 if (selection->selected (*i)) {
104 if (passed_clicked) {
113 passed_clicked = false;
117 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
120 passed_clicked = true;
124 if (passed_clicked) {
125 if ((*i)->hidden()) {
128 if (selection->selected (*i)) {
130 } else if (!(*i)->hidden()) {
131 to_be_added.push_back (*i);
138 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
141 passed_clicked = true;
145 if (passed_clicked) {
147 if ((*r)->hidden()) {
151 if (selection->selected (*r)) {
153 } else if (!(*r)->hidden()) {
154 to_be_added.push_back (*r);
160 if (!to_be_added.empty()) {
161 selection->add (to_be_added);
169 Editor::select_all_tracks ()
171 TrackViewList visible_views;
172 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
173 if ((*i)->marked_for_display()) {
174 visible_views.push_back (*i);
177 selection->set (visible_views);
180 /** Select clicked_axisview, unless there are no currently selected
181 * tracks, in which case nothing will happen unless `force' is true.
184 Editor::set_selected_track_as_side_effect (Selection::Operation op)
186 if (!clicked_axisview) {
190 if (!clicked_routeview) {
194 bool had_tracks = !selection->tracks.empty();
195 RouteGroup* group = clicked_routeview->route()->route_group();
196 RouteGroup& arg (_session->all_route_group());
199 case Selection::Toggle:
200 if (selection->selected (clicked_axisview)) {
201 if (arg.is_select() && arg.is_active()) {
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203 selection->remove(*i);
205 } else if (group && group->is_active()) {
206 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
207 if ((*i)->route_group() == group)
208 selection->remove(*i);
211 selection->remove (clicked_axisview);
214 if (arg.is_select() && arg.is_active()) {
215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
218 } else if (group && group->is_active()) {
219 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
220 if ( (*i)->route_group() == group)
224 selection->add (clicked_axisview);
230 if (!had_tracks && arg.is_select() && arg.is_active()) {
231 /* nothing was selected already, and all group is active etc. so use
234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
237 } else if (group && group->is_active()) {
238 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 if ((*i)->route_group() == group)
243 selection->add (clicked_axisview);
249 if (!had_tracks && arg.is_select() && arg.is_active()) {
250 /* nothing was selected already, and all group is active etc. so use
253 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
256 } else if (group && group->is_active()) {
257 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
258 if ((*i)->route_group() == group)
262 selection->set (clicked_axisview);
266 case Selection::Extend:
268 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
274 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
277 case Selection::Toggle:
278 if (selection->selected (&view)) {
280 selection->remove (&view);
283 selection->add (&view);
288 if (!selection->selected (&view)) {
289 selection->add (&view);
294 selection->set (&view);
297 case Selection::Extend:
298 extend_selection_to_track (view);
304 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
306 if (!clicked_routeview) {
314 set_selected_track (*clicked_routeview, op, no_remove);
318 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
320 if (!clicked_control_point) {
326 selection->set (clicked_control_point);
329 selection->add (clicked_control_point);
331 case Selection::Toggle:
332 selection->toggle (clicked_control_point);
334 case Selection::Extend:
343 Editor::get_onscreen_tracks (TrackViewList& tvl)
345 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
346 if ((*i)->y_position() < _canvas_height) {
352 /** Call a slot for a given `basis' track and also for any track that is in the same
353 * active route group with a particular set of properties.
355 * @param sl Slot to call.
356 * @param basis Basis track.
357 * @param prop Properties that active edit groups must share to be included in the map.
361 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
363 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
365 if (route_basis == 0) {
369 set<RouteTimeAxisView*> tracks;
370 tracks.insert (route_basis);
372 RouteGroup* group = route_basis->route()->route_group();
374 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
376 /* the basis is a member of an active route group, with the appropriate
377 properties; find other members */
379 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
380 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
381 if (v && v->route()->route_group() == group) {
388 uint32_t const sz = tracks.size ();
390 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
395 /** Call a slot for a given `basis' track and also for any track that is in the same
396 * active route group with a particular set of properties.
398 * @param sl Slot to call.
399 * @param basis Basis track.
400 * @param prop Properties that active edit groups must share to be included in the map.
404 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
406 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
407 set<boost::shared_ptr<Playlist> > playlists;
409 if (route_basis == 0) {
413 set<RouteTimeAxisView*> tracks;
414 tracks.insert (route_basis);
416 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
418 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
420 /* the basis is a member of an active route group, with the appropriate
421 properties; find other members */
423 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
424 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
426 if (v && v->route()->route_group() == group) {
428 boost::shared_ptr<Track> t = v->track();
430 if (playlists.insert (t->playlist()).second) {
431 /* haven't seen this playlist yet */
435 /* not actually a "Track", but a timeaxis view that
436 we should mapover anyway.
445 uint32_t const sz = tracks.size ();
447 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
453 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
455 boost::shared_ptr<Playlist> pl;
456 vector<boost::shared_ptr<Region> > results;
458 boost::shared_ptr<Track> tr;
460 if ((tr = tv.track()) == 0) {
465 if (&tv == &basis->get_time_axis_view()) {
466 /* looking in same track as the original */
470 if ((pl = tr->playlist()) != 0) {
471 pl->get_equivalent_regions (basis->region(), results);
474 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
475 if ((marv = tv.view()->find_view (*ir)) != 0) {
476 all_equivs->push_back (marv);
482 Editor::mapped_get_equivalent_crossfades (
483 RouteTimeAxisView& tv, uint32_t, boost::shared_ptr<Crossfade> basis, vector<boost::shared_ptr<Crossfade> >* equivs
486 boost::shared_ptr<Playlist> pl;
487 vector<boost::shared_ptr<Crossfade> > results;
488 boost::shared_ptr<Track> tr;
490 if ((tr = tv.track()) == 0) {
495 if ((pl = tr->playlist()) != 0) {
496 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
498 apl->get_equivalent_crossfades (basis, *equivs);
502 /* We might have just checked basis for equivalency with itself, so we need to remove dupes */
503 sort (equivs->begin (), equivs->end ());
504 unique (equivs->begin (), equivs->end ());
508 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
510 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);
512 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
514 equivalent_regions.push_back (basis);
518 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
520 RegionSelection equivalent;
522 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
524 vector<RegionView*> eq;
526 mapover_tracks_with_unique_playlists (
527 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
528 &(*i)->get_time_axis_view(), prop);
530 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
540 vector<boost::shared_ptr<Crossfade> >
541 Editor::get_equivalent_crossfades (RouteTimeAxisView& v, boost::shared_ptr<Crossfade> c, PBD::PropertyID prop) const
543 vector<boost::shared_ptr<Crossfade> > e;
544 mapover_tracks_with_unique_playlists (
545 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_crossfades), c, &e),
554 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
556 int region_count = 0;
558 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
560 RouteTimeAxisView* tatv;
562 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
564 boost::shared_ptr<Playlist> pl;
565 vector<boost::shared_ptr<Region> > results;
567 boost::shared_ptr<Track> tr;
569 if ((tr = tatv->track()) == 0) {
574 if ((pl = (tr->playlist())) != 0) {
575 pl->get_region_list_equivalent_regions (region, results);
578 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
579 if ((marv = tatv->view()->find_view (*ir)) != 0) {
592 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
594 vector<RegionView*> all_equivalent_regions;
597 if (!clicked_regionview || !clicked_routeview) {
602 button_release_can_deselect = false;
605 if (op == Selection::Toggle || op == Selection::Set) {
608 case Selection::Toggle:
609 if (selection->selected (clicked_regionview)) {
612 /* whatever was clicked was selected already; do nothing here but allow
613 the button release to deselect it
616 button_release_can_deselect = true;
619 if (button_release_can_deselect) {
621 /* just remove this one region, but only on a permitted button release */
623 selection->remove (clicked_regionview);
626 /* no more deselect action on button release till a new press
627 finds an already selected object.
630 button_release_can_deselect = false;
638 if (selection->selected (clicked_routeview)) {
639 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
641 all_equivalent_regions.push_back (clicked_regionview);
644 /* add all the equivalent regions, but only on button press */
646 if (!all_equivalent_regions.empty()) {
650 selection->add (all_equivalent_regions);
656 if (!selection->selected (clicked_regionview)) {
657 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
658 selection->set (all_equivalent_regions);
661 /* no commit necessary: clicked on an already selected region */
671 } else if (op == Selection::Extend) {
673 list<Selectable*> results;
674 framepos_t last_frame;
675 framepos_t first_frame;
676 bool same_track = false;
678 /* 1. find the last selected regionview in the track that was clicked in */
681 first_frame = max_framepos;
683 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
684 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
686 if ((*x)->region()->last_frame() > last_frame) {
687 last_frame = (*x)->region()->last_frame();
690 if ((*x)->region()->first_frame() < first_frame) {
691 first_frame = (*x)->region()->first_frame();
700 /* 2. figure out the boundaries for our search for new objects */
702 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
704 if (last_frame < clicked_regionview->region()->first_frame()) {
705 first_frame = last_frame;
706 last_frame = clicked_regionview->region()->last_frame();
708 last_frame = first_frame;
709 first_frame = clicked_regionview->region()->first_frame();
713 case OverlapExternal:
714 if (last_frame < clicked_regionview->region()->first_frame()) {
715 first_frame = last_frame;
716 last_frame = clicked_regionview->region()->last_frame();
718 last_frame = first_frame;
719 first_frame = clicked_regionview->region()->first_frame();
723 case OverlapInternal:
724 if (last_frame < clicked_regionview->region()->first_frame()) {
725 first_frame = last_frame;
726 last_frame = clicked_regionview->region()->last_frame();
728 last_frame = first_frame;
729 first_frame = clicked_regionview->region()->first_frame();
735 /* nothing to do except add clicked region to selection, since it
736 overlaps with the existing selection in this track.
743 /* click in a track that has no regions selected, so extend vertically
744 to pick out all regions that are defined by the existing selection
749 first_frame = clicked_regionview->region()->position();
750 last_frame = clicked_regionview->region()->last_frame();
752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
753 if ((*i)->region()->position() < first_frame) {
754 first_frame = (*i)->region()->position();
756 if ((*i)->region()->last_frame() + 1 > last_frame) {
757 last_frame = (*i)->region()->last_frame();
762 /* 2. find all the tracks we should select in */
764 set<RouteTimeAxisView*> relevant_tracks;
766 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
767 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
769 relevant_tracks.insert (r);
773 set<RouteTimeAxisView*> already_in_selection;
775 if (relevant_tracks.empty()) {
777 /* no tracks selected .. thus .. if the
778 regionview we're in isn't selected
779 (i.e. we're about to extend to it), then
780 find all tracks between the this one and
784 if (!selection->selected (clicked_regionview)) {
786 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
790 /* add this track to the ones we will search */
792 relevant_tracks.insert (rtv);
794 /* find the track closest to this one that
795 already a selected region.
798 RouteTimeAxisView* closest = 0;
799 int distance = INT_MAX;
800 int key = rtv->route()->order_key ("editor");
802 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
804 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
806 if (artv && artv != rtv) {
808 pair<set<RouteTimeAxisView*>::iterator,bool> result;
810 result = already_in_selection.insert (artv);
813 /* newly added to already_in_selection */
815 int d = artv->route()->order_key ("editor");
819 if (abs (d) < distance) {
829 /* now add all tracks between that one and this one */
831 int okey = closest->route()->order_key ("editor");
837 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
838 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
839 if (artv && artv != rtv) {
841 int k = artv->route()->order_key ("editor");
843 if (k >= okey && k <= key) {
845 /* in range but don't add it if
846 it already has tracks selected.
847 this avoids odd selection
848 behaviour that feels wrong.
851 if (find (already_in_selection.begin(),
852 already_in_selection.end(),
853 artv) == already_in_selection.end()) {
855 relevant_tracks.insert (artv);
865 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
866 one that was clicked.
869 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
870 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
873 /* 4. convert to a vector of regions */
875 vector<RegionView*> regions;
877 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
880 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
881 regions.push_back (arv);
885 if (!regions.empty()) {
886 selection->add (regions);
897 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
899 vector<RegionView*> all_equivalent_regions;
901 get_regions_corresponding_to (region, all_equivalent_regions);
903 if (all_equivalent_regions.empty()) {
907 begin_reversible_command (_("set selected regions"));
910 case Selection::Toggle:
911 /* XXX this is not correct */
912 selection->toggle (all_equivalent_regions);
915 selection->set (all_equivalent_regions);
917 case Selection::Extend:
918 selection->add (all_equivalent_regions);
921 selection->add (all_equivalent_regions);
925 commit_reversible_command () ;
929 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
932 boost::shared_ptr<Region> r (weak_r.lock());
938 if ((rv = sv->find_view (r)) == 0) {
942 /* don't reset the selection if its something other than
943 a single other region.
946 if (selection->regions.size() > 1) {
950 begin_reversible_command (_("set selected regions"));
954 commit_reversible_command () ;
960 Editor::track_selection_changed ()
962 switch (selection->tracks.size()) {
966 set_selected_mixer_strip (*(selection->tracks.front()));
970 RouteNotificationListPtr routes (new RouteNotificationList);
972 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
974 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
976 (*i)->set_selected (yn);
978 TimeAxisView::Children c = (*i)->get_child_list ();
979 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
980 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
984 ((mouse_mode == MouseRange) ||
985 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
986 (*i)->reshow_selection (selection->time);
988 (*i)->hide_selection ();
993 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
995 routes->push_back (rtav->route());
1000 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1002 /* notify control protocols */
1004 ControlProtocol::TrackSelectionChanged (routes);
1008 Editor::time_selection_changed ()
1010 if (Profile->get_sae()) {
1014 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1015 (*i)->hide_selection ();
1018 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1019 (*i)->show_selection (selection->time);
1022 if (selection->time.empty()) {
1023 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1025 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1028 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
1029 _session->request_locate (selection->time.start());
1033 /** Set all region actions to have a given sensitivity */
1035 Editor::sensitize_all_region_actions (bool s)
1037 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1039 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1040 (*i)->set_sensitive (s);
1043 _all_region_actions_sensitized = s;
1046 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1047 * This method should be called just before displaying a Region menu. When a Region menu is not
1048 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1049 * on entered_regionviews work without having to check sensitivity every time the selection or
1050 * entered_regionview changes.
1052 * This method also sets up toggle action state as appropriate.
1055 Editor::sensitize_the_right_region_actions ()
1057 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1058 sensitize_all_region_actions (false);
1059 if (!selection->time.empty()) {
1060 _region_actions->get_action("split-region")->set_sensitive (true);
1064 } else if (mouse_mode != MouseObject) {
1065 sensitize_all_region_actions (false);
1069 /* We get here if we are in Object mode */
1071 RegionSelection rs = get_regions_from_selection_and_entered ();
1072 sensitize_all_region_actions (!rs.empty ());
1074 _ignore_region_action = true;
1076 /* Look through the regions that are selected and make notes about what we have got */
1078 bool have_audio = false;
1079 bool have_multichannel_audio = false;
1080 bool have_midi = false;
1081 bool have_locked = false;
1082 bool have_unlocked = false;
1083 bool have_position_lock_style_audio = false;
1084 bool have_position_lock_style_music = false;
1085 bool have_muted = false;
1086 bool have_unmuted = false;
1087 bool have_opaque = false;
1088 bool have_non_opaque = false;
1089 bool have_not_at_natural_position = false;
1090 bool have_envelope_active = false;
1091 bool have_envelope_inactive = false;
1092 bool have_non_unity_scale_amplitude = false;
1093 bool have_compound_regions = false;
1095 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1097 boost::shared_ptr<Region> r = (*i)->region ();
1098 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1102 if (ar->n_channels() > 1) {
1103 have_multichannel_audio = true;
1107 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1111 if (r->is_compound()) {
1112 have_compound_regions = true;
1118 have_unlocked = true;
1121 if (r->position_lock_style() == MusicTime) {
1122 have_position_lock_style_music = true;
1124 have_position_lock_style_audio = true;
1130 have_unmuted = true;
1136 have_non_opaque = true;
1139 if (!r->at_natural_position()) {
1140 have_not_at_natural_position = true;
1144 if (ar->envelope_active()) {
1145 have_envelope_active = true;
1147 have_envelope_inactive = true;
1150 if (ar->scale_amplitude() != 1) {
1151 have_non_unity_scale_amplitude = 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 _region_actions->get_action("combine-regions")->set_sensitive (true);
1163 _region_actions->get_action("combine-regions")->set_sensitive (false);
1165 } else if (rs.size() == 1) {
1166 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1167 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1168 _region_actions->get_action("combine-regions")->set_sensitive (false);
1171 if (!have_multichannel_audio) {
1172 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1176 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1177 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1178 _region_actions->get_action("quantize-region")->set_sensitive (false);
1179 _region_actions->get_action("fork-region")->set_sensitive (false);
1180 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1181 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1182 _region_actions->get_action("transpose-region")->set_sensitive (false);
1184 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1185 /* others were already marked sensitive */
1188 if (_edit_point == EditAtMouse) {
1189 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1190 _region_actions->get_action("trim-front")->set_sensitive (false);
1191 _region_actions->get_action("trim-back")->set_sensitive (false);
1192 _region_actions->get_action("split-region")->set_sensitive (false);
1193 _region_actions->get_action("place-transient")->set_sensitive (false);
1196 if (have_compound_regions) {
1197 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1199 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1204 if (have_envelope_active && !have_envelope_inactive) {
1205 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1206 } else if (have_envelope_active && have_envelope_inactive) {
1207 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1212 _region_actions->get_action("analyze-region")->set_sensitive (false);
1213 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1214 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1215 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1219 if (!have_non_unity_scale_amplitude || !have_audio) {
1220 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1223 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1224 if (have_locked && have_unlocked) {
1225 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1228 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1230 if (have_position_lock_style_music && have_position_lock_style_audio) {
1231 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1234 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1235 if (have_muted && have_unmuted) {
1236 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1239 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1240 if (have_opaque && have_non_opaque) {
1241 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1244 if (!have_not_at_natural_position) {
1245 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1248 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1249 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1250 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1252 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1255 _ignore_region_action = false;
1257 _all_region_actions_sensitized = false;
1262 Editor::region_selection_changed ()
1264 _regions->block_change_connection (true);
1265 editor_regions_selection_changed_connection.block(true);
1267 if (_region_selection_change_updates_region_list) {
1268 _regions->unselect_all ();
1271 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1272 (*i)->set_selected_regionviews (selection->regions);
1275 if (_region_selection_change_updates_region_list) {
1276 _regions->set_selected (selection->regions);
1279 _regions->block_change_connection (false);
1280 editor_regions_selection_changed_connection.block(false);
1282 if (!_all_region_actions_sensitized) {
1283 /* This selection change might have changed what region actions
1284 are allowed, so sensitize them all in case a key is pressed.
1286 sensitize_all_region_actions (true);
1289 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1290 _session->request_locate (selection->regions.start());
1295 Editor::point_selection_changed ()
1297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1298 (*i)->set_selected_points (selection->points);
1303 Editor::select_all_in_track (Selection::Operation op)
1305 list<Selectable *> touched;
1307 if (!clicked_routeview) {
1311 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1314 case Selection::Toggle:
1315 selection->add (touched);
1317 case Selection::Set:
1318 selection->set (touched);
1320 case Selection::Extend:
1321 /* meaningless, because we're selecting everything */
1323 case Selection::Add:
1324 selection->add (touched);
1330 Editor::select_all_internal_edit (Selection::Operation)
1332 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1333 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1335 mrv->select_all_notes ();
1341 Editor::select_all (Selection::Operation op)
1343 list<Selectable *> touched;
1345 if (_internal_editing) {
1346 select_all_internal_edit (op);
1350 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1351 if ((*iter)->hidden()) {
1354 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1356 begin_reversible_command (_("select all"));
1358 case Selection::Add:
1359 selection->add (touched);
1361 case Selection::Toggle:
1362 selection->add (touched);
1364 case Selection::Set:
1365 selection->set (touched);
1367 case Selection::Extend:
1368 /* meaningless, because we're selecting everything */
1371 commit_reversible_command ();
1375 Editor::invert_selection_in_track ()
1377 list<Selectable *> touched;
1379 if (!clicked_routeview) {
1383 clicked_routeview->get_inverted_selectables (*selection, touched);
1384 selection->set (touched);
1388 Editor::invert_selection ()
1390 list<Selectable *> touched;
1392 if (_internal_editing) {
1393 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1394 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1396 mrv->invert_selection ();
1402 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1403 if ((*iter)->hidden()) {
1406 (*iter)->get_inverted_selectables (*selection, touched);
1409 selection->set (touched);
1412 /** @param start Start time in session frames.
1413 * @param end End time in session frames.
1414 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1415 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1416 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1417 * within the region are already selected.
1420 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1422 list<Selectable*> found;
1424 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1426 if ((*iter)->hidden()) {
1430 (*iter)->get_selectables (start, end, top, bot, found);
1433 if (found.empty()) {
1437 if (preserve_if_selected && op != Selection::Toggle) {
1438 list<Selectable*>::iterator i = found.begin();
1439 while (i != found.end() && (*i)->get_selected()) {
1443 if (i == found.end()) {
1448 begin_reversible_command (_("select all within"));
1450 case Selection::Add:
1451 selection->add (found);
1453 case Selection::Toggle:
1454 selection->toggle (found);
1456 case Selection::Set:
1457 selection->set (found);
1459 case Selection::Extend:
1460 /* not defined yet */
1464 commit_reversible_command ();
1468 Editor::set_selection_from_region ()
1470 if (selection->regions.empty()) {
1474 selection->set (selection->regions.start(), selection->regions.end_frame());
1475 if (!Profile->get_sae()) {
1476 set_mouse_mode (Editing::MouseRange, false);
1481 Editor::set_selection_from_punch()
1485 if ((location = _session->locations()->auto_punch_location()) == 0) {
1489 set_selection_from_range (*location);
1493 Editor::set_selection_from_loop()
1497 if ((location = _session->locations()->auto_loop_location()) == 0) {
1500 set_selection_from_range (*location);
1504 Editor::set_selection_from_range (Location& loc)
1506 begin_reversible_command (_("set selection from range"));
1507 selection->set (loc.start(), loc.end());
1508 commit_reversible_command ();
1510 if (!Profile->get_sae()) {
1511 set_mouse_mode (Editing::MouseRange, false);
1516 Editor::select_all_selectables_using_time_selection ()
1518 list<Selectable *> touched;
1520 if (selection->time.empty()) {
1524 framepos_t start = selection->time[clicked_selection].start;
1525 framepos_t end = selection->time[clicked_selection].end;
1527 if (end - start < 1) {
1533 if (selection->tracks.empty()) {
1536 ts = &selection->tracks;
1539 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1540 if ((*iter)->hidden()) {
1543 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1546 begin_reversible_command (_("select all from range"));
1547 selection->set (touched);
1548 commit_reversible_command ();
1553 Editor::select_all_selectables_using_punch()
1555 Location* location = _session->locations()->auto_punch_location();
1556 list<Selectable *> touched;
1558 if (location == 0 || (location->end() - location->start() <= 1)) {
1565 if (selection->tracks.empty()) {
1568 ts = &selection->tracks;
1571 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1572 if ((*iter)->hidden()) {
1575 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1577 begin_reversible_command (_("select all from punch"));
1578 selection->set (touched);
1579 commit_reversible_command ();
1584 Editor::select_all_selectables_using_loop()
1586 Location* location = _session->locations()->auto_loop_location();
1587 list<Selectable *> touched;
1589 if (location == 0 || (location->end() - location->start() <= 1)) {
1596 if (selection->tracks.empty()) {
1599 ts = &selection->tracks;
1602 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1603 if ((*iter)->hidden()) {
1606 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1608 begin_reversible_command (_("select all from loop"));
1609 selection->set (touched);
1610 commit_reversible_command ();
1615 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1619 list<Selectable *> touched;
1622 start = cursor->current_frame;
1623 end = _session->current_end_frame();
1625 if (cursor->current_frame > 0) {
1627 end = cursor->current_frame - 1;
1633 if (_internal_editing) {
1634 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1635 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1637 mrv->select_range (start, end);
1644 begin_reversible_command (_("select all after cursor"));
1646 begin_reversible_command (_("select all before cursor"));
1651 if (selection->tracks.empty()) {
1654 ts = &selection->tracks;
1657 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1658 if ((*iter)->hidden()) {
1661 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1663 selection->set (touched);
1664 commit_reversible_command ();
1668 Editor::select_all_selectables_using_edit (bool after)
1672 list<Selectable *> touched;
1675 start = get_preferred_edit_position();
1676 end = _session->current_end_frame();
1678 if ((end = get_preferred_edit_position()) > 1) {
1686 if (_internal_editing) {
1687 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1688 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1689 mrv->select_range (start, end);
1695 begin_reversible_command (_("select all after edit"));
1697 begin_reversible_command (_("select all before edit"));
1702 if (selection->tracks.empty()) {
1705 ts = &selection->tracks;
1708 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1709 if ((*iter)->hidden()) {
1712 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1714 selection->set (touched);
1715 commit_reversible_command ();
1719 Editor::select_all_selectables_between (bool /*within*/)
1723 list<Selectable *> touched;
1725 if (!get_edit_op_range (start, end)) {
1729 if (_internal_editing) {
1730 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1731 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1732 mrv->select_range (start, end);
1739 if (selection->tracks.empty()) {
1742 ts = &selection->tracks;
1745 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1746 if ((*iter)->hidden()) {
1749 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1752 selection->set (touched);
1756 Editor::select_range_between ()
1761 if (mouse_mode == MouseRange && !selection->time.empty()) {
1762 selection->clear_time ();
1765 if (!get_edit_op_range (start, end)) {
1769 set_mouse_mode (MouseRange);
1770 selection->set (start, end);
1774 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1779 /* in range mode, use any existing selection */
1781 if (mouse_mode == MouseRange && !selection->time.empty()) {
1782 /* we know that these are ordered */
1783 start = selection->time.start();
1784 end = selection->time.end_frame();
1788 if (!mouse_frame (m, ignored)) {
1789 /* mouse is not in a canvas, try playhead+selected marker.
1790 this is probably most true when using menus.
1793 if (selection->markers.empty()) {
1797 start = selection->markers.front()->position();
1798 end = _session->audible_frame();
1802 switch (_edit_point) {
1803 case EditAtPlayhead:
1804 if (selection->markers.empty()) {
1805 /* use mouse + playhead */
1807 end = _session->audible_frame();
1809 /* use playhead + selected marker */
1810 start = _session->audible_frame();
1811 end = selection->markers.front()->position();
1816 /* use mouse + selected marker */
1817 if (selection->markers.empty()) {
1819 end = _session->audible_frame();
1821 start = selection->markers.front()->position();
1826 case EditAtSelectedMarker:
1827 /* use mouse + selected marker */
1828 if (selection->markers.empty()) {
1830 MessageDialog win (_("No edit range defined"),
1835 win.set_secondary_text (
1836 _("the edit point is Selected Marker\nbut there is no selected marker."));
1839 win.set_default_response (RESPONSE_CLOSE);
1840 win.set_position (Gtk::WIN_POS_MOUSE);
1845 return false; // NO RANGE
1847 start = selection->markers.front()->position();
1861 /* turn range into one delimited by start...end,
1871 Editor::deselect_all ()
1873 selection->clear ();
1877 Editor::select_range (framepos_t s, framepos_t e)
1879 selection->add (clicked_axisview);
1880 selection->time.clear ();
1881 return selection->set (s, e);