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_visible = false;
1075 bool have_envelope_invisible = false;
1076 bool have_envelope_active = false;
1077 bool have_envelope_inactive = false;
1078 bool have_non_unity_scale_amplitude = false;
1079 bool have_compound_regions = false;
1081 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1083 boost::shared_ptr<Region> r = (*i)->region ();
1084 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1088 if (ar->n_channels() > 1) {
1089 have_multichannel_audio = true;
1093 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1097 if (r->is_compound()) {
1098 have_compound_regions = true;
1104 have_unlocked = true;
1107 if (r->position_lock_style() == MusicTime) {
1108 have_position_lock_style_music = true;
1110 have_position_lock_style_audio = true;
1116 have_unmuted = true;
1122 have_non_opaque = true;
1125 if (!r->at_natural_position()) {
1126 have_not_at_natural_position = true;
1130 /* its a bit unfortunate that "envelope visible" is a view-only
1131 property. we have to find the regionview to able to check
1132 its current setting.
1135 have_envelope_invisible = false;
1138 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1140 if (arv->envelope_visible()) {
1141 have_envelope_visible = true;
1143 have_envelope_invisible = true;
1148 if (ar->envelope_active()) {
1149 have_envelope_active = true;
1151 have_envelope_inactive = true;
1154 if (ar->scale_amplitude() != 1) {
1155 have_non_unity_scale_amplitude = true;
1160 if (rs.size() > 1) {
1161 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1162 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1163 _region_actions->get_action("rename-region")->set_sensitive (false);
1165 _region_actions->get_action("combine-regions")->set_sensitive (true);
1167 _region_actions->get_action("combine-regions")->set_sensitive (false);
1169 } else if (rs.size() == 1) {
1170 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1171 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1172 _region_actions->get_action("combine-regions")->set_sensitive (false);
1175 if (!have_multichannel_audio) {
1176 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1180 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1181 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1182 _region_actions->get_action("quantize-region")->set_sensitive (false);
1183 _region_actions->get_action("fork-region")->set_sensitive (false);
1184 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1185 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1186 _region_actions->get_action("transpose-region")->set_sensitive (false);
1188 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1189 /* others were already marked sensitive */
1192 if (_edit_point == EditAtMouse) {
1193 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1194 _region_actions->get_action("trim-front")->set_sensitive (false);
1195 _region_actions->get_action("trim-back")->set_sensitive (false);
1196 _region_actions->get_action("split-region")->set_sensitive (false);
1197 _region_actions->get_action("place-transient")->set_sensitive (false);
1200 if (have_compound_regions) {
1201 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1203 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1208 if (have_envelope_visible && !have_envelope_invisible) {
1209 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1210 } else if (have_envelope_visible && have_envelope_invisible) {
1211 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1214 if (have_envelope_active && !have_envelope_inactive) {
1215 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1216 } else if (have_envelope_active && have_envelope_inactive) {
1217 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1222 _region_actions->get_action("analyze-region")->set_sensitive (false);
1223 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1224 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1225 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1226 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1230 if (!have_non_unity_scale_amplitude || !have_audio) {
1231 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1234 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1235 if (have_locked && have_unlocked) {
1236 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1239 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);
1241 if (have_position_lock_style_music && have_position_lock_style_audio) {
1242 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1245 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1246 if (have_muted && have_unmuted) {
1247 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1250 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1251 if (have_opaque && have_non_opaque) {
1252 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1255 if (!have_not_at_natural_position) {
1256 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1259 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1260 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1261 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1263 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1266 _ignore_region_action = false;
1268 _all_region_actions_sensitized = false;
1273 Editor::region_selection_changed ()
1275 _regions->block_change_connection (true);
1276 editor_regions_selection_changed_connection.block(true);
1278 if (_region_selection_change_updates_region_list) {
1279 _regions->unselect_all ();
1282 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1283 (*i)->set_selected_regionviews (selection->regions);
1286 if (_region_selection_change_updates_region_list) {
1287 _regions->set_selected (selection->regions);
1290 _regions->block_change_connection (false);
1291 editor_regions_selection_changed_connection.block(false);
1293 if (!_all_region_actions_sensitized) {
1294 /* This selection change might have changed what region actions
1295 are allowed, so sensitize them all in case a key is pressed.
1297 sensitize_all_region_actions (true);
1300 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1301 _session->request_locate (selection->regions.start());
1306 Editor::point_selection_changed ()
1308 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1309 (*i)->set_selected_points (selection->points);
1314 Editor::select_all_in_track (Selection::Operation op)
1316 list<Selectable *> touched;
1318 if (!clicked_routeview) {
1322 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1325 case Selection::Toggle:
1326 selection->add (touched);
1328 case Selection::Set:
1329 selection->set (touched);
1331 case Selection::Extend:
1332 /* meaningless, because we're selecting everything */
1334 case Selection::Add:
1335 selection->add (touched);
1341 Editor::select_all_internal_edit (Selection::Operation)
1343 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1344 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1346 mrv->select_all_notes ();
1352 Editor::select_all (Selection::Operation op)
1354 list<Selectable *> touched;
1356 if (_internal_editing) {
1357 select_all_internal_edit (op);
1361 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1362 if ((*iter)->hidden()) {
1365 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1367 begin_reversible_command (_("select all"));
1369 case Selection::Add:
1370 selection->add (touched);
1372 case Selection::Toggle:
1373 selection->add (touched);
1375 case Selection::Set:
1376 selection->set (touched);
1378 case Selection::Extend:
1379 /* meaningless, because we're selecting everything */
1382 commit_reversible_command ();
1386 Editor::invert_selection_in_track ()
1388 list<Selectable *> touched;
1390 if (!clicked_routeview) {
1394 clicked_routeview->get_inverted_selectables (*selection, touched);
1395 selection->set (touched);
1399 Editor::invert_selection ()
1401 list<Selectable *> touched;
1403 if (_internal_editing) {
1404 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1405 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1407 mrv->invert_selection ();
1413 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1414 if ((*iter)->hidden()) {
1417 (*iter)->get_inverted_selectables (*selection, touched);
1420 selection->set (touched);
1423 /** @param start Start time in session frames.
1424 * @param end End time in session frames.
1425 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1426 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1427 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1428 * within the region are already selected.
1431 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1433 list<Selectable*> found;
1435 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1437 if ((*iter)->hidden()) {
1441 (*iter)->get_selectables (start, end, top, bot, found);
1444 if (found.empty()) {
1448 if (preserve_if_selected && op != Selection::Toggle) {
1449 list<Selectable*>::iterator i = found.begin();
1450 while (i != found.end() && (*i)->get_selected()) {
1454 if (i == found.end()) {
1459 begin_reversible_command (_("select all within"));
1461 case Selection::Add:
1462 selection->add (found);
1464 case Selection::Toggle:
1465 selection->toggle (found);
1467 case Selection::Set:
1468 selection->set (found);
1470 case Selection::Extend:
1471 /* not defined yet */
1475 commit_reversible_command ();
1479 Editor::set_selection_from_region ()
1481 if (selection->regions.empty()) {
1485 selection->set (selection->regions.start(), selection->regions.end_frame());
1486 if (!Profile->get_sae()) {
1487 set_mouse_mode (Editing::MouseRange, false);
1492 Editor::set_selection_from_punch()
1496 if ((location = _session->locations()->auto_punch_location()) == 0) {
1500 set_selection_from_range (*location);
1504 Editor::set_selection_from_loop()
1508 if ((location = _session->locations()->auto_loop_location()) == 0) {
1511 set_selection_from_range (*location);
1515 Editor::set_selection_from_range (Location& loc)
1517 begin_reversible_command (_("set selection from range"));
1518 selection->set (loc.start(), loc.end());
1519 commit_reversible_command ();
1521 if (!Profile->get_sae()) {
1522 set_mouse_mode (Editing::MouseRange, false);
1527 Editor::select_all_selectables_using_time_selection ()
1529 list<Selectable *> touched;
1531 if (selection->time.empty()) {
1535 framepos_t start = selection->time[clicked_selection].start;
1536 framepos_t end = selection->time[clicked_selection].end;
1538 if (end - start < 1) {
1544 if (selection->tracks.empty()) {
1547 ts = &selection->tracks;
1550 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1551 if ((*iter)->hidden()) {
1554 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1557 begin_reversible_command (_("select all from range"));
1558 selection->set (touched);
1559 commit_reversible_command ();
1564 Editor::select_all_selectables_using_punch()
1566 Location* location = _session->locations()->auto_punch_location();
1567 list<Selectable *> touched;
1569 if (location == 0 || (location->end() - location->start() <= 1)) {
1576 if (selection->tracks.empty()) {
1579 ts = &selection->tracks;
1582 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1583 if ((*iter)->hidden()) {
1586 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1588 begin_reversible_command (_("select all from punch"));
1589 selection->set (touched);
1590 commit_reversible_command ();
1595 Editor::select_all_selectables_using_loop()
1597 Location* location = _session->locations()->auto_loop_location();
1598 list<Selectable *> touched;
1600 if (location == 0 || (location->end() - location->start() <= 1)) {
1607 if (selection->tracks.empty()) {
1610 ts = &selection->tracks;
1613 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1614 if ((*iter)->hidden()) {
1617 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1619 begin_reversible_command (_("select all from loop"));
1620 selection->set (touched);
1621 commit_reversible_command ();
1626 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1630 list<Selectable *> touched;
1633 start = cursor->current_frame;
1634 end = _session->current_end_frame();
1636 if (cursor->current_frame > 0) {
1638 end = cursor->current_frame - 1;
1644 if (_internal_editing) {
1645 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1646 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1648 mrv->select_range (start, end);
1655 begin_reversible_command (_("select all after cursor"));
1657 begin_reversible_command (_("select all before cursor"));
1662 if (selection->tracks.empty()) {
1665 ts = &selection->tracks;
1668 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1669 if ((*iter)->hidden()) {
1672 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1674 selection->set (touched);
1675 commit_reversible_command ();
1679 Editor::select_all_selectables_using_edit (bool after)
1683 list<Selectable *> touched;
1686 start = get_preferred_edit_position();
1687 end = _session->current_end_frame();
1689 if ((end = get_preferred_edit_position()) > 1) {
1697 if (_internal_editing) {
1698 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1699 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1700 mrv->select_range (start, end);
1706 begin_reversible_command (_("select all after edit"));
1708 begin_reversible_command (_("select all before edit"));
1713 if (selection->tracks.empty()) {
1716 ts = &selection->tracks;
1719 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1720 if ((*iter)->hidden()) {
1723 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1725 selection->set (touched);
1726 commit_reversible_command ();
1730 Editor::select_all_selectables_between (bool /*within*/)
1734 list<Selectable *> touched;
1736 if (!get_edit_op_range (start, end)) {
1740 if (_internal_editing) {
1741 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1742 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1743 mrv->select_range (start, end);
1750 if (selection->tracks.empty()) {
1753 ts = &selection->tracks;
1756 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1757 if ((*iter)->hidden()) {
1760 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1763 selection->set (touched);
1767 Editor::select_range_between ()
1772 if (mouse_mode == MouseRange && !selection->time.empty()) {
1773 selection->clear_time ();
1776 if (!get_edit_op_range (start, end)) {
1780 set_mouse_mode (MouseRange);
1781 selection->set (start, end);
1785 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1790 /* in range mode, use any existing selection */
1792 if (mouse_mode == MouseRange && !selection->time.empty()) {
1793 /* we know that these are ordered */
1794 start = selection->time.start();
1795 end = selection->time.end_frame();
1799 if (!mouse_frame (m, ignored)) {
1800 /* mouse is not in a canvas, try playhead+selected marker.
1801 this is probably most true when using menus.
1804 if (selection->markers.empty()) {
1808 start = selection->markers.front()->position();
1809 end = _session->audible_frame();
1813 switch (_edit_point) {
1814 case EditAtPlayhead:
1815 if (selection->markers.empty()) {
1816 /* use mouse + playhead */
1818 end = _session->audible_frame();
1820 /* use playhead + selected marker */
1821 start = _session->audible_frame();
1822 end = selection->markers.front()->position();
1827 /* use mouse + selected marker */
1828 if (selection->markers.empty()) {
1830 end = _session->audible_frame();
1832 start = selection->markers.front()->position();
1837 case EditAtSelectedMarker:
1838 /* use mouse + selected marker */
1839 if (selection->markers.empty()) {
1841 MessageDialog win (_("No edit range defined"),
1846 win.set_secondary_text (
1847 _("the edit point is Selected Marker\nbut there is no selected marker."));
1850 win.set_default_response (RESPONSE_CLOSE);
1851 win.set_position (Gtk::WIN_POS_MOUSE);
1856 return false; // NO RANGE
1858 start = selection->markers.front()->position();
1872 /* turn range into one delimited by start...end,
1882 Editor::deselect_all ()
1884 selection->clear ();
1888 Editor::select_range (framepos_t s, framepos_t e)
1890 selection->add (clicked_axisview);
1891 selection->time.clear ();
1892 return selection->set (s, e);