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"
34 #include "audio_time_axis.h"
35 #include "audio_region_view.h"
36 #include "audio_streamview.h"
37 #include "automation_line.h"
38 #include "control_point.h"
39 #include "crossfade_view.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:
266 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
272 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
275 case Selection::Toggle:
276 if (selection->selected (&view)) {
278 selection->remove (&view);
281 selection->add (&view);
286 if (!selection->selected (&view)) {
287 selection->add (&view);
292 selection->set (&view);
295 case Selection::Extend:
296 extend_selection_to_track (view);
302 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
304 if (!clicked_routeview) {
312 set_selected_track (*clicked_routeview, op, no_remove);
316 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
318 if (!clicked_control_point) {
324 selection->set (clicked_control_point);
327 selection->add (clicked_control_point);
329 case Selection::Toggle:
330 selection->toggle (clicked_control_point);
332 case Selection::Extend:
341 Editor::get_onscreen_tracks (TrackViewList& tvl)
343 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
344 if ((*i)->y_position() < _canvas_height) {
350 /** Call a slot for a given `basis' track and also for any track that is in the same
351 * active route group with a particular set of properties.
353 * @param sl Slot to call.
354 * @param basis Basis track.
355 * @param prop Properties that active edit groups must share to be included in the map.
359 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
361 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
363 if (route_basis == 0) {
367 set<RouteTimeAxisView*> tracks;
368 tracks.insert (route_basis);
370 RouteGroup* group = route_basis->route()->route_group();
372 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
374 /* the basis is a member of an active route group, with the appropriate
375 properties; find other members */
377 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
378 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
379 if (v && v->route()->route_group() == group) {
386 uint32_t const sz = tracks.size ();
388 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
393 /** Call a slot for a given `basis' track and also for any track that is in the same
394 * active route group with a particular set of properties.
396 * @param sl Slot to call.
397 * @param basis Basis track.
398 * @param prop Properties that active edit groups must share to be included in the map.
402 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
404 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
405 set<boost::shared_ptr<Playlist> > playlists;
407 if (route_basis == 0) {
411 set<RouteTimeAxisView*> tracks;
412 tracks.insert (route_basis);
414 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
416 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
418 /* the basis is a member of an active route group, with the appropriate
419 properties; find other members */
421 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
422 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
424 if (v && v->route()->route_group() == group) {
426 boost::shared_ptr<Track> t = v->track();
428 if (playlists.insert (t->playlist()).second) {
429 /* haven't seen this playlist yet */
433 /* not actually a "Track", but a timeaxis view that
434 we should mapover anyway.
443 uint32_t const sz = tracks.size ();
445 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
451 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
453 boost::shared_ptr<Playlist> pl;
454 vector<boost::shared_ptr<Region> > results;
456 boost::shared_ptr<Track> tr;
458 if ((tr = tv.track()) == 0) {
463 if (&tv == &basis->get_time_axis_view()) {
464 /* looking in same track as the original */
468 if ((pl = tr->playlist()) != 0) {
469 pl->get_equivalent_regions (basis->region(), results);
472 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
473 if ((marv = tv.view()->find_view (*ir)) != 0) {
474 all_equivs->push_back (marv);
480 Editor::mapped_get_equivalent_crossfades (
481 RouteTimeAxisView& tv, uint32_t, boost::shared_ptr<Crossfade> basis, vector<boost::shared_ptr<Crossfade> >* equivs
484 boost::shared_ptr<Playlist> pl;
485 vector<boost::shared_ptr<Crossfade> > results;
486 boost::shared_ptr<Track> tr;
488 if ((tr = tv.track()) == 0) {
493 if ((pl = tr->playlist()) != 0) {
494 boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
496 apl->get_equivalent_crossfades (basis, *equivs);
500 /* We might have just checked basis for equivalency with itself, so we need to remove dupes */
501 sort (equivs->begin (), equivs->end ());
502 unique (equivs->begin (), equivs->end ());
506 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
508 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);
510 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
512 equivalent_regions.push_back (basis);
516 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
518 RegionSelection equivalent;
520 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
522 vector<RegionView*> eq;
524 mapover_tracks_with_unique_playlists (
525 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
526 &(*i)->get_time_axis_view(), prop);
528 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
538 vector<boost::shared_ptr<Crossfade> >
539 Editor::get_equivalent_crossfades (RouteTimeAxisView& v, boost::shared_ptr<Crossfade> c, PBD::PropertyID prop) const
541 vector<boost::shared_ptr<Crossfade> > e;
542 mapover_tracks_with_unique_playlists (
543 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_crossfades), c, &e),
552 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
554 int region_count = 0;
556 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
558 RouteTimeAxisView* tatv;
560 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
562 boost::shared_ptr<Playlist> pl;
563 vector<boost::shared_ptr<Region> > results;
565 boost::shared_ptr<Track> tr;
567 if ((tr = tatv->track()) == 0) {
572 if ((pl = (tr->playlist())) != 0) {
573 pl->get_region_list_equivalent_regions (region, results);
576 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
577 if ((marv = tatv->view()->find_view (*ir)) != 0) {
590 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
592 vector<RegionView*> all_equivalent_regions;
595 if (!clicked_regionview || !clicked_routeview) {
600 button_release_can_deselect = false;
603 if (op == Selection::Toggle || op == Selection::Set) {
606 case Selection::Toggle:
607 if (selection->selected (clicked_regionview)) {
610 /* whatever was clicked was selected already; do nothing here but allow
611 the button release to deselect it
614 button_release_can_deselect = true;
617 if (button_release_can_deselect) {
619 /* just remove this one region, but only on a permitted button release */
621 selection->remove (clicked_regionview);
624 /* no more deselect action on button release till a new press
625 finds an already selected object.
628 button_release_can_deselect = false;
636 if (selection->selected (clicked_routeview)) {
637 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
639 all_equivalent_regions.push_back (clicked_regionview);
642 /* add all the equivalent regions, but only on button press */
644 if (!all_equivalent_regions.empty()) {
648 selection->add (all_equivalent_regions);
654 if (!selection->selected (clicked_regionview)) {
655 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
656 selection->set (all_equivalent_regions);
659 /* no commit necessary: clicked on an already selected region */
669 } else if (op == Selection::Extend) {
671 list<Selectable*> results;
672 framepos_t last_frame;
673 framepos_t first_frame;
674 bool same_track = false;
676 /* 1. find the last selected regionview in the track that was clicked in */
679 first_frame = max_framepos;
681 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
682 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
684 if ((*x)->region()->last_frame() > last_frame) {
685 last_frame = (*x)->region()->last_frame();
688 if ((*x)->region()->first_frame() < first_frame) {
689 first_frame = (*x)->region()->first_frame();
698 /* 2. figure out the boundaries for our search for new objects */
700 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
702 if (last_frame < clicked_regionview->region()->first_frame()) {
703 first_frame = last_frame;
704 last_frame = clicked_regionview->region()->last_frame();
706 last_frame = first_frame;
707 first_frame = clicked_regionview->region()->first_frame();
711 case OverlapExternal:
712 if (last_frame < clicked_regionview->region()->first_frame()) {
713 first_frame = last_frame;
714 last_frame = clicked_regionview->region()->last_frame();
716 last_frame = first_frame;
717 first_frame = clicked_regionview->region()->first_frame();
721 case OverlapInternal:
722 if (last_frame < clicked_regionview->region()->first_frame()) {
723 first_frame = last_frame;
724 last_frame = clicked_regionview->region()->last_frame();
726 last_frame = first_frame;
727 first_frame = clicked_regionview->region()->first_frame();
733 /* nothing to do except add clicked region to selection, since it
734 overlaps with the existing selection in this track.
741 /* click in a track that has no regions selected, so extend vertically
742 to pick out all regions that are defined by the existing selection
747 first_frame = clicked_regionview->region()->position();
748 last_frame = clicked_regionview->region()->last_frame();
750 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
751 if ((*i)->region()->position() < first_frame) {
752 first_frame = (*i)->region()->position();
754 if ((*i)->region()->last_frame() + 1 > last_frame) {
755 last_frame = (*i)->region()->last_frame();
760 /* 2. find all the tracks we should select in */
762 set<RouteTimeAxisView*> relevant_tracks;
764 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
765 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
767 relevant_tracks.insert (r);
771 set<RouteTimeAxisView*> already_in_selection;
773 if (relevant_tracks.empty()) {
775 /* no tracks selected .. thus .. if the
776 regionview we're in isn't selected
777 (i.e. we're about to extend to it), then
778 find all tracks between the this one and
782 if (!selection->selected (clicked_regionview)) {
784 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
788 /* add this track to the ones we will search */
790 relevant_tracks.insert (rtv);
792 /* find the track closest to this one that
793 already a selected region.
796 RouteTimeAxisView* closest = 0;
797 int distance = INT_MAX;
798 int key = rtv->route()->order_key ("editor");
800 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
802 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
804 if (artv && artv != rtv) {
806 pair<set<RouteTimeAxisView*>::iterator,bool> result;
808 result = already_in_selection.insert (artv);
811 /* newly added to already_in_selection */
813 int d = artv->route()->order_key ("editor");
817 if (abs (d) < distance) {
827 /* now add all tracks between that one and this one */
829 int okey = closest->route()->order_key ("editor");
835 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
836 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
837 if (artv && artv != rtv) {
839 int k = artv->route()->order_key ("editor");
841 if (k >= okey && k <= key) {
843 /* in range but don't add it if
844 it already has tracks selected.
845 this avoids odd selection
846 behaviour that feels wrong.
849 if (find (already_in_selection.begin(),
850 already_in_selection.end(),
851 artv) == already_in_selection.end()) {
853 relevant_tracks.insert (artv);
863 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
864 one that was clicked.
867 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
868 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
871 /* 4. convert to a vector of regions */
873 vector<RegionView*> regions;
875 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
878 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
879 regions.push_back (arv);
883 if (!regions.empty()) {
884 selection->add (regions);
895 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
897 vector<RegionView*> all_equivalent_regions;
899 get_regions_corresponding_to (region, all_equivalent_regions);
901 if (all_equivalent_regions.empty()) {
905 begin_reversible_command (_("set selected regions"));
908 case Selection::Toggle:
909 /* XXX this is not correct */
910 selection->toggle (all_equivalent_regions);
913 selection->set (all_equivalent_regions);
915 case Selection::Extend:
916 selection->add (all_equivalent_regions);
919 selection->add (all_equivalent_regions);
923 commit_reversible_command () ;
927 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
930 boost::shared_ptr<Region> r (weak_r.lock());
936 if ((rv = sv->find_view (r)) == 0) {
940 /* don't reset the selection if its something other than
941 a single other region.
944 if (selection->regions.size() > 1) {
948 begin_reversible_command (_("set selected regions"));
952 commit_reversible_command () ;
958 Editor::track_selection_changed ()
960 switch (selection->tracks.size()) {
964 set_selected_mixer_strip (*(selection->tracks.front()));
968 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
970 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
972 (*i)->set_selected (yn);
974 TimeAxisView::Children c = (*i)->get_child_list ();
975 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
976 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
980 ((mouse_mode == MouseRange) ||
981 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
982 (*i)->reshow_selection (selection->time);
984 (*i)->hide_selection ();
988 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
992 Editor::time_selection_changed ()
994 if (Profile->get_sae()) {
998 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
999 (*i)->hide_selection ();
1002 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1003 (*i)->show_selection (selection->time);
1006 if (selection->time.empty()) {
1007 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1009 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1012 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
1013 _session->request_locate (selection->time.start());
1017 /** Set all region actions to have a given sensitivity */
1019 Editor::sensitize_all_region_actions (bool s)
1021 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1023 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1024 (*i)->set_sensitive (s);
1027 _all_region_actions_sensitized = s;
1030 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1031 * This method should be called just before displaying a Region menu. When a Region menu is not
1032 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1033 * on entered_regionviews work without having to check sensitivity every time the selection or
1034 * entered_regionview changes.
1036 * This method also sets up toggle action state as appropriate.
1039 Editor::sensitize_the_right_region_actions ()
1041 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1042 sensitize_all_region_actions (false);
1043 if (!selection->time.empty()) {
1044 _region_actions->get_action("split-region")->set_sensitive (true);
1048 } else if (mouse_mode != MouseObject) {
1049 sensitize_all_region_actions (false);
1053 /* We get here if we are in Object mode */
1055 RegionSelection rs = get_regions_from_selection_and_entered ();
1056 sensitize_all_region_actions (!rs.empty ());
1058 _ignore_region_action = true;
1060 /* Look through the regions that are selected and make notes about what we have got */
1062 bool have_audio = false;
1063 bool have_multichannel_audio = false;
1064 bool have_midi = false;
1065 bool have_locked = false;
1066 bool have_unlocked = false;
1067 bool have_position_lock_style_audio = false;
1068 bool have_position_lock_style_music = false;
1069 bool have_muted = false;
1070 bool have_unmuted = false;
1071 bool have_opaque = false;
1072 bool have_non_opaque = false;
1073 bool have_not_at_natural_position = false;
1074 bool have_envelope_active = false;
1075 bool have_envelope_inactive = false;
1076 bool have_non_unity_scale_amplitude = false;
1077 bool have_compound_regions = false;
1079 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1081 boost::shared_ptr<Region> r = (*i)->region ();
1082 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1086 if (ar->n_channels() > 1) {
1087 have_multichannel_audio = true;
1091 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1095 if (r->is_compound()) {
1096 have_compound_regions = true;
1102 have_unlocked = true;
1105 if (r->position_lock_style() == MusicTime) {
1106 have_position_lock_style_music = true;
1108 have_position_lock_style_audio = true;
1114 have_unmuted = true;
1120 have_non_opaque = true;
1123 if (!r->at_natural_position()) {
1124 have_not_at_natural_position = true;
1128 if (ar->envelope_active()) {
1129 have_envelope_active = true;
1131 have_envelope_inactive = true;
1134 if (ar->scale_amplitude() != 1) {
1135 have_non_unity_scale_amplitude = true;
1140 if (rs.size() > 1) {
1141 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1142 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1143 _region_actions->get_action("rename-region")->set_sensitive (false);
1145 _region_actions->get_action("combine-regions")->set_sensitive (true);
1147 _region_actions->get_action("combine-regions")->set_sensitive (false);
1149 } else if (rs.size() == 1) {
1150 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1151 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1152 _region_actions->get_action("combine-regions")->set_sensitive (false);
1155 if (!have_multichannel_audio) {
1156 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1160 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1161 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1162 _region_actions->get_action("quantize-region")->set_sensitive (false);
1163 _region_actions->get_action("fork-region")->set_sensitive (false);
1164 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1165 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1166 _region_actions->get_action("transpose-region")->set_sensitive (false);
1168 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1169 /* others were already marked sensitive */
1172 if (_edit_point == EditAtMouse) {
1173 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1174 _region_actions->get_action("trim-front")->set_sensitive (false);
1175 _region_actions->get_action("trim-back")->set_sensitive (false);
1176 _region_actions->get_action("split-region")->set_sensitive (false);
1177 _region_actions->get_action("place-transient")->set_sensitive (false);
1180 if (have_compound_regions) {
1181 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1183 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1188 if (have_envelope_active && !have_envelope_inactive) {
1189 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1190 } else if (have_envelope_active && have_envelope_inactive) {
1191 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1196 _region_actions->get_action("analyze-region")->set_sensitive (false);
1197 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1198 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1199 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1203 if (!have_non_unity_scale_amplitude || !have_audio) {
1204 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1207 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1208 if (have_locked && have_unlocked) {
1209 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1212 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);
1214 if (have_position_lock_style_music && have_position_lock_style_audio) {
1215 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1218 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1219 if (have_muted && have_unmuted) {
1220 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1223 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1224 if (have_opaque && have_non_opaque) {
1225 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1228 if (!have_not_at_natural_position) {
1229 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1232 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1233 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1234 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1236 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1239 _ignore_region_action = false;
1241 _all_region_actions_sensitized = false;
1246 Editor::region_selection_changed ()
1248 _regions->block_change_connection (true);
1249 editor_regions_selection_changed_connection.block(true);
1251 if (_region_selection_change_updates_region_list) {
1252 _regions->unselect_all ();
1255 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1256 (*i)->set_selected_regionviews (selection->regions);
1259 if (_region_selection_change_updates_region_list) {
1260 _regions->set_selected (selection->regions);
1263 _regions->block_change_connection (false);
1264 editor_regions_selection_changed_connection.block(false);
1266 if (!_all_region_actions_sensitized) {
1267 /* This selection change might have changed what region actions
1268 are allowed, so sensitize them all in case a key is pressed.
1270 sensitize_all_region_actions (true);
1273 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1274 _session->request_locate (selection->regions.start());
1279 Editor::point_selection_changed ()
1281 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1282 (*i)->set_selected_points (selection->points);
1287 Editor::select_all_in_track (Selection::Operation op)
1289 list<Selectable *> touched;
1291 if (!clicked_routeview) {
1295 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1298 case Selection::Toggle:
1299 selection->add (touched);
1301 case Selection::Set:
1302 selection->set (touched);
1304 case Selection::Extend:
1305 /* meaningless, because we're selecting everything */
1307 case Selection::Add:
1308 selection->add (touched);
1314 Editor::select_all_internal_edit (Selection::Operation)
1316 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1317 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1319 mrv->select_all_notes ();
1325 Editor::select_all (Selection::Operation op)
1327 list<Selectable *> touched;
1329 if (_internal_editing) {
1330 select_all_internal_edit (op);
1334 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1335 if ((*iter)->hidden()) {
1338 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1340 begin_reversible_command (_("select all"));
1342 case Selection::Add:
1343 selection->add (touched);
1345 case Selection::Toggle:
1346 selection->add (touched);
1348 case Selection::Set:
1349 selection->set (touched);
1351 case Selection::Extend:
1352 /* meaningless, because we're selecting everything */
1355 commit_reversible_command ();
1359 Editor::invert_selection_in_track ()
1361 list<Selectable *> touched;
1363 if (!clicked_routeview) {
1367 clicked_routeview->get_inverted_selectables (*selection, touched);
1368 selection->set (touched);
1372 Editor::invert_selection ()
1374 list<Selectable *> touched;
1376 if (_internal_editing) {
1377 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1378 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1380 mrv->invert_selection ();
1386 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1387 if ((*iter)->hidden()) {
1390 (*iter)->get_inverted_selectables (*selection, touched);
1393 selection->set (touched);
1396 /** @param start Start time in session frames.
1397 * @param end End time in session frames.
1398 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1399 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1400 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1401 * within the region are already selected.
1404 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1406 list<Selectable*> found;
1408 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1410 if ((*iter)->hidden()) {
1414 (*iter)->get_selectables (start, end, top, bot, found);
1417 if (found.empty()) {
1421 if (preserve_if_selected && op != Selection::Toggle) {
1422 list<Selectable*>::iterator i = found.begin();
1423 while (i != found.end() && (*i)->get_selected()) {
1427 if (i == found.end()) {
1432 begin_reversible_command (_("select all within"));
1434 case Selection::Add:
1435 selection->add (found);
1437 case Selection::Toggle:
1438 selection->toggle (found);
1440 case Selection::Set:
1441 selection->set (found);
1443 case Selection::Extend:
1444 /* not defined yet */
1448 commit_reversible_command ();
1452 Editor::set_selection_from_region ()
1454 if (selection->regions.empty()) {
1458 selection->set (selection->regions.start(), selection->regions.end_frame());
1459 if (!Profile->get_sae()) {
1460 set_mouse_mode (Editing::MouseRange, false);
1465 Editor::set_selection_from_punch()
1469 if ((location = _session->locations()->auto_punch_location()) == 0) {
1473 set_selection_from_range (*location);
1477 Editor::set_selection_from_loop()
1481 if ((location = _session->locations()->auto_loop_location()) == 0) {
1484 set_selection_from_range (*location);
1488 Editor::set_selection_from_range (Location& loc)
1490 begin_reversible_command (_("set selection from range"));
1491 selection->set (loc.start(), loc.end());
1492 commit_reversible_command ();
1494 if (!Profile->get_sae()) {
1495 set_mouse_mode (Editing::MouseRange, false);
1500 Editor::select_all_selectables_using_time_selection ()
1502 list<Selectable *> touched;
1504 if (selection->time.empty()) {
1508 framepos_t start = selection->time[clicked_selection].start;
1509 framepos_t end = selection->time[clicked_selection].end;
1511 if (end - start < 1) {
1517 if (selection->tracks.empty()) {
1520 ts = &selection->tracks;
1523 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1524 if ((*iter)->hidden()) {
1527 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1530 begin_reversible_command (_("select all from range"));
1531 selection->set (touched);
1532 commit_reversible_command ();
1537 Editor::select_all_selectables_using_punch()
1539 Location* location = _session->locations()->auto_punch_location();
1540 list<Selectable *> touched;
1542 if (location == 0 || (location->end() - location->start() <= 1)) {
1549 if (selection->tracks.empty()) {
1552 ts = &selection->tracks;
1555 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1556 if ((*iter)->hidden()) {
1559 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1561 begin_reversible_command (_("select all from punch"));
1562 selection->set (touched);
1563 commit_reversible_command ();
1568 Editor::select_all_selectables_using_loop()
1570 Location* location = _session->locations()->auto_loop_location();
1571 list<Selectable *> touched;
1573 if (location == 0 || (location->end() - location->start() <= 1)) {
1580 if (selection->tracks.empty()) {
1583 ts = &selection->tracks;
1586 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1587 if ((*iter)->hidden()) {
1590 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1592 begin_reversible_command (_("select all from loop"));
1593 selection->set (touched);
1594 commit_reversible_command ();
1599 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1603 list<Selectable *> touched;
1606 start = cursor->current_frame;
1607 end = _session->current_end_frame();
1609 if (cursor->current_frame > 0) {
1611 end = cursor->current_frame - 1;
1617 if (_internal_editing) {
1618 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1619 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1621 mrv->select_range (start, end);
1628 begin_reversible_command (_("select all after cursor"));
1630 begin_reversible_command (_("select all before cursor"));
1635 if (selection->tracks.empty()) {
1638 ts = &selection->tracks;
1641 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1642 if ((*iter)->hidden()) {
1645 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1647 selection->set (touched);
1648 commit_reversible_command ();
1652 Editor::select_all_selectables_using_edit (bool after)
1656 list<Selectable *> touched;
1659 start = get_preferred_edit_position();
1660 end = _session->current_end_frame();
1662 if ((end = get_preferred_edit_position()) > 1) {
1670 if (_internal_editing) {
1671 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1672 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1673 mrv->select_range (start, end);
1679 begin_reversible_command (_("select all after edit"));
1681 begin_reversible_command (_("select all before edit"));
1686 if (selection->tracks.empty()) {
1689 ts = &selection->tracks;
1692 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1693 if ((*iter)->hidden()) {
1696 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1698 selection->set (touched);
1699 commit_reversible_command ();
1703 Editor::select_all_selectables_between (bool /*within*/)
1707 list<Selectable *> touched;
1709 if (!get_edit_op_range (start, end)) {
1713 if (_internal_editing) {
1714 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1715 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1716 mrv->select_range (start, end);
1723 if (selection->tracks.empty()) {
1726 ts = &selection->tracks;
1729 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1730 if ((*iter)->hidden()) {
1733 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1736 selection->set (touched);
1740 Editor::select_range_between ()
1745 if (mouse_mode == MouseRange && !selection->time.empty()) {
1746 selection->clear_time ();
1749 if (!get_edit_op_range (start, end)) {
1753 set_mouse_mode (MouseRange);
1754 selection->set (start, end);
1758 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1763 /* in range mode, use any existing selection */
1765 if (mouse_mode == MouseRange && !selection->time.empty()) {
1766 /* we know that these are ordered */
1767 start = selection->time.start();
1768 end = selection->time.end_frame();
1772 if (!mouse_frame (m, ignored)) {
1773 /* mouse is not in a canvas, try playhead+selected marker.
1774 this is probably most true when using menus.
1777 if (selection->markers.empty()) {
1781 start = selection->markers.front()->position();
1782 end = _session->audible_frame();
1786 switch (_edit_point) {
1787 case EditAtPlayhead:
1788 if (selection->markers.empty()) {
1789 /* use mouse + playhead */
1791 end = _session->audible_frame();
1793 /* use playhead + selected marker */
1794 start = _session->audible_frame();
1795 end = selection->markers.front()->position();
1800 /* use mouse + selected marker */
1801 if (selection->markers.empty()) {
1803 end = _session->audible_frame();
1805 start = selection->markers.front()->position();
1810 case EditAtSelectedMarker:
1811 /* use mouse + selected marker */
1812 if (selection->markers.empty()) {
1814 MessageDialog win (_("No edit range defined"),
1819 win.set_secondary_text (
1820 _("the edit point is Selected Marker\nbut there is no selected marker."));
1823 win.set_default_response (RESPONSE_CLOSE);
1824 win.set_position (Gtk::WIN_POS_MOUSE);
1829 return false; // NO RANGE
1831 start = selection->markers.front()->position();
1845 /* turn range into one delimited by start...end,
1855 Editor::deselect_all ()
1857 selection->clear ();
1861 Editor::select_range (framepos_t s, framepos_t e)
1863 selection->add (clicked_axisview);
1864 selection->time.clear ();
1865 return selection->set (s, e);