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 "editor_regions.h"
42 #include "editor_cursors.h"
43 #include "midi_region_view.h"
48 using namespace ARDOUR;
52 using namespace Gtkmm2ext;
53 using namespace Editing;
55 struct TrackViewByPositionSorter
57 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
58 return a->y_position() < b->y_position();
63 Editor::extend_selection_to_track (TimeAxisView& view)
65 if (selection->selected (&view)) {
66 /* already selected, do nothing */
70 if (selection->tracks.empty()) {
72 if (!selection->selected (&view)) {
73 selection->set (&view);
80 /* something is already selected, so figure out which range of things to add */
82 TrackViewList to_be_added;
83 TrackViewList sorted = track_views;
84 TrackViewByPositionSorter cmp;
85 bool passed_clicked = false;
90 if (!selection->selected (&view)) {
91 to_be_added.push_back (&view);
94 /* figure out if we should go forward or backwards */
96 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
99 passed_clicked = true;
102 if (selection->selected (*i)) {
103 if (passed_clicked) {
112 passed_clicked = false;
116 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
119 passed_clicked = true;
123 if (passed_clicked) {
124 if ((*i)->hidden()) {
127 if (selection->selected (*i)) {
129 } else if (!(*i)->hidden()) {
130 to_be_added.push_back (*i);
137 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
140 passed_clicked = true;
144 if (passed_clicked) {
146 if ((*r)->hidden()) {
150 if (selection->selected (*r)) {
152 } else if (!(*r)->hidden()) {
153 to_be_added.push_back (*r);
159 if (!to_be_added.empty()) {
160 selection->add (to_be_added);
168 Editor::select_all_tracks ()
170 TrackViewList visible_views;
171 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
172 if ((*i)->marked_for_display()) {
173 visible_views.push_back (*i);
176 selection->set (visible_views);
179 /** Select clicked_axisview, unless there are no currently selected
180 * tracks, in which case nothing will happen unless `force' is true.
183 Editor::set_selected_track_as_side_effect (Selection::Operation op)
185 if (!clicked_axisview) {
189 if (!clicked_routeview) {
193 bool had_tracks = !selection->tracks.empty();
194 RouteGroup* group = clicked_routeview->route()->route_group();
195 RouteGroup& arg (_session->all_route_group());
198 case Selection::Toggle:
199 if (selection->selected (clicked_axisview)) {
200 if (arg.is_select() && arg.is_active()) {
201 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
202 selection->remove(*i);
204 } else if (group && group->is_active()) {
205 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
206 if ((*i)->route_group() == group)
207 selection->remove(*i);
210 selection->remove (clicked_axisview);
213 if (arg.is_select() && arg.is_active()) {
214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217 } else if (group && group->is_active()) {
218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 if ( (*i)->route_group() == group)
223 selection->add (clicked_axisview);
229 if (!had_tracks && arg.is_select() && arg.is_active()) {
230 /* nothing was selected already, and all group is active etc. so use
233 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 } else if (group && group->is_active()) {
237 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
238 if ((*i)->route_group() == group)
242 selection->add (clicked_axisview);
248 if (!had_tracks && arg.is_select() && arg.is_active()) {
249 /* nothing was selected already, and all group is active etc. so use
252 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
255 } else if (group && group->is_active()) {
256 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
257 if ((*i)->route_group() == group)
261 selection->set (clicked_axisview);
265 case Selection::Extend:
267 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
273 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
276 case Selection::Toggle:
277 if (selection->selected (&view)) {
279 selection->remove (&view);
282 selection->add (&view);
287 if (!selection->selected (&view)) {
288 selection->add (&view);
293 selection->set (&view);
296 case Selection::Extend:
297 extend_selection_to_track (view);
303 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
305 if (!clicked_routeview) {
313 set_selected_track (*clicked_routeview, op, no_remove);
317 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
319 if (!clicked_control_point) {
329 selection->set (clicked_control_point);
332 selection->add (clicked_control_point);
334 case Selection::Toggle:
335 selection->toggle (clicked_control_point);
337 case Selection::Extend:
346 Editor::get_onscreen_tracks (TrackViewList& tvl)
348 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
349 if ((*i)->y_position() < _canvas_height) {
355 /** Call a slot for a given `basis' track and also for any track that is in the same
356 * active route group with a particular set of properties.
358 * @param sl Slot to call.
359 * @param basis Basis track.
360 * @param prop Properties that active edit groups must share to be included in the map.
364 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
366 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
368 if (route_basis == 0) {
372 set<RouteTimeAxisView*> tracks;
373 tracks.insert (route_basis);
375 RouteGroup* group = route_basis->route()->route_group();
377 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
379 /* the basis is a member of an active route group, with the appropriate
380 properties; find other members */
382 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
383 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
384 if (v && v->route()->route_group() == group) {
391 uint32_t const sz = tracks.size ();
393 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
398 /** Call a slot for a given `basis' track and also for any track that is in the same
399 * active route group with a particular set of properties.
401 * @param sl Slot to call.
402 * @param basis Basis track.
403 * @param prop Properties that active edit groups must share to be included in the map.
407 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
409 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
410 set<boost::shared_ptr<Playlist> > playlists;
412 if (route_basis == 0) {
416 set<RouteTimeAxisView*> tracks;
417 tracks.insert (route_basis);
419 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
421 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
423 /* the basis is a member of an active route group, with the appropriate
424 properties; find other members */
426 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
427 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
429 if (v && v->route()->route_group() == group) {
431 boost::shared_ptr<Track> t = v->track();
433 if (playlists.insert (t->playlist()).second) {
434 /* haven't seen this playlist yet */
438 /* not actually a "Track", but a timeaxis view that
439 we should mapover anyway.
448 uint32_t const sz = tracks.size ();
450 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
456 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
458 boost::shared_ptr<Playlist> pl;
459 vector<boost::shared_ptr<Region> > results;
461 boost::shared_ptr<Track> tr;
463 if ((tr = tv.track()) == 0) {
468 if (&tv == &basis->get_time_axis_view()) {
469 /* looking in same track as the original */
473 if ((pl = tr->playlist()) != 0) {
474 pl->get_equivalent_regions (basis->region(), results);
477 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
478 if ((marv = tv.view()->find_view (*ir)) != 0) {
479 all_equivs->push_back (marv);
485 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
487 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);
489 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
491 equivalent_regions.push_back (basis);
495 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
497 RegionSelection equivalent;
499 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
501 vector<RegionView*> eq;
503 mapover_tracks_with_unique_playlists (
504 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
505 &(*i)->get_time_axis_view(), prop);
507 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
518 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
520 int region_count = 0;
522 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
524 RouteTimeAxisView* tatv;
526 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
528 boost::shared_ptr<Playlist> pl;
529 vector<boost::shared_ptr<Region> > results;
531 boost::shared_ptr<Track> tr;
533 if ((tr = tatv->track()) == 0) {
538 if ((pl = (tr->playlist())) != 0) {
539 pl->get_region_list_equivalent_regions (region, results);
542 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
543 if ((marv = tatv->view()->find_view (*ir)) != 0) {
556 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
558 vector<RegionView*> all_equivalent_regions;
561 if (!clicked_regionview || !clicked_routeview) {
566 button_release_can_deselect = false;
569 if (op == Selection::Toggle || op == Selection::Set) {
572 case Selection::Toggle:
573 if (selection->selected (clicked_regionview)) {
576 /* whatever was clicked was selected already; do nothing here but allow
577 the button release to deselect it
580 button_release_can_deselect = true;
583 if (button_release_can_deselect) {
585 /* just remove this one region, but only on a permitted button release */
587 selection->remove (clicked_regionview);
590 /* no more deselect action on button release till a new press
591 finds an already selected object.
594 button_release_can_deselect = false;
602 if (selection->selected (clicked_routeview)) {
603 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
605 all_equivalent_regions.push_back (clicked_regionview);
608 /* add all the equivalent regions, but only on button press */
610 if (!all_equivalent_regions.empty()) {
614 selection->add (all_equivalent_regions);
620 if (!selection->selected (clicked_regionview)) {
621 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
622 selection->set (all_equivalent_regions);
625 /* no commit necessary: clicked on an already selected region */
635 } else if (op == Selection::Extend) {
637 list<Selectable*> results;
638 framepos_t last_frame;
639 framepos_t first_frame;
640 bool same_track = false;
642 /* 1. find the last selected regionview in the track that was clicked in */
645 first_frame = max_framepos;
647 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
648 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
650 if ((*x)->region()->last_frame() > last_frame) {
651 last_frame = (*x)->region()->last_frame();
654 if ((*x)->region()->first_frame() < first_frame) {
655 first_frame = (*x)->region()->first_frame();
664 /* 2. figure out the boundaries for our search for new objects */
666 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
667 case Evoral::OverlapNone:
668 if (last_frame < clicked_regionview->region()->first_frame()) {
669 first_frame = last_frame;
670 last_frame = clicked_regionview->region()->last_frame();
672 last_frame = first_frame;
673 first_frame = clicked_regionview->region()->first_frame();
677 case Evoral::OverlapExternal:
678 if (last_frame < clicked_regionview->region()->first_frame()) {
679 first_frame = last_frame;
680 last_frame = clicked_regionview->region()->last_frame();
682 last_frame = first_frame;
683 first_frame = clicked_regionview->region()->first_frame();
687 case Evoral::OverlapInternal:
688 if (last_frame < clicked_regionview->region()->first_frame()) {
689 first_frame = last_frame;
690 last_frame = clicked_regionview->region()->last_frame();
692 last_frame = first_frame;
693 first_frame = clicked_regionview->region()->first_frame();
697 case Evoral::OverlapStart:
698 case Evoral::OverlapEnd:
699 /* nothing to do except add clicked region to selection, since it
700 overlaps with the existing selection in this track.
707 /* click in a track that has no regions selected, so extend vertically
708 to pick out all regions that are defined by the existing selection
713 first_frame = clicked_regionview->region()->position();
714 last_frame = clicked_regionview->region()->last_frame();
716 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
717 if ((*i)->region()->position() < first_frame) {
718 first_frame = (*i)->region()->position();
720 if ((*i)->region()->last_frame() + 1 > last_frame) {
721 last_frame = (*i)->region()->last_frame();
726 /* 2. find all the tracks we should select in */
728 set<RouteTimeAxisView*> relevant_tracks;
730 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
731 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
733 relevant_tracks.insert (r);
737 set<RouteTimeAxisView*> already_in_selection;
739 if (relevant_tracks.empty()) {
741 /* no tracks selected .. thus .. if the
742 regionview we're in isn't selected
743 (i.e. we're about to extend to it), then
744 find all tracks between the this one and
748 if (!selection->selected (clicked_regionview)) {
750 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
754 /* add this track to the ones we will search */
756 relevant_tracks.insert (rtv);
758 /* find the track closest to this one that
759 already a selected region.
762 RouteTimeAxisView* closest = 0;
763 int distance = INT_MAX;
764 int key = rtv->route()->order_key ("editor");
766 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
768 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
770 if (artv && artv != rtv) {
772 pair<set<RouteTimeAxisView*>::iterator,bool> result;
774 result = already_in_selection.insert (artv);
777 /* newly added to already_in_selection */
779 int d = artv->route()->order_key ("editor");
783 if (abs (d) < distance) {
793 /* now add all tracks between that one and this one */
795 int okey = closest->route()->order_key ("editor");
801 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
802 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
803 if (artv && artv != rtv) {
805 int k = artv->route()->order_key ("editor");
807 if (k >= okey && k <= key) {
809 /* in range but don't add it if
810 it already has tracks selected.
811 this avoids odd selection
812 behaviour that feels wrong.
815 if (find (already_in_selection.begin(),
816 already_in_selection.end(),
817 artv) == already_in_selection.end()) {
819 relevant_tracks.insert (artv);
829 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
830 one that was clicked.
833 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
834 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
837 /* 4. convert to a vector of regions */
839 vector<RegionView*> regions;
841 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
844 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
845 regions.push_back (arv);
849 if (!regions.empty()) {
850 selection->add (regions);
861 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
863 vector<RegionView*> all_equivalent_regions;
865 get_regions_corresponding_to (region, all_equivalent_regions);
867 if (all_equivalent_regions.empty()) {
871 begin_reversible_command (_("set selected regions"));
874 case Selection::Toggle:
875 /* XXX this is not correct */
876 selection->toggle (all_equivalent_regions);
879 selection->set (all_equivalent_regions);
881 case Selection::Extend:
882 selection->add (all_equivalent_regions);
885 selection->add (all_equivalent_regions);
889 commit_reversible_command () ;
893 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
896 boost::shared_ptr<Region> r (weak_r.lock());
902 if ((rv = sv->find_view (r)) == 0) {
906 /* don't reset the selection if its something other than
907 a single other region.
910 if (selection->regions.size() > 1) {
914 begin_reversible_command (_("set selected regions"));
918 commit_reversible_command () ;
924 Editor::track_selection_changed ()
926 switch (selection->tracks.size()) {
930 set_selected_mixer_strip (*(selection->tracks.front()));
934 RouteNotificationListPtr routes (new RouteNotificationList);
936 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
938 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
940 (*i)->set_selected (yn);
942 TimeAxisView::Children c = (*i)->get_child_list ();
943 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
944 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
948 ((mouse_mode == MouseRange) ||
949 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
950 (*i)->reshow_selection (selection->time);
952 (*i)->hide_selection ();
957 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
959 routes->push_back (rtav->route());
964 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
966 /* notify control protocols */
968 ControlProtocol::TrackSelectionChanged (routes);
972 Editor::time_selection_changed ()
974 if (Profile->get_sae()) {
978 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
979 (*i)->hide_selection ();
982 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
983 (*i)->show_selection (selection->time);
986 if (selection->time.empty()) {
987 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
989 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
992 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
993 _session->request_locate (selection->time.start());
997 /** Set all region actions to have a given sensitivity */
999 Editor::sensitize_all_region_actions (bool s)
1001 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1003 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1004 (*i)->set_sensitive (s);
1007 _all_region_actions_sensitized = s;
1010 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1011 * This method should be called just before displaying a Region menu. When a Region menu is not
1012 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1013 * on entered_regionviews work without having to check sensitivity every time the selection or
1014 * entered_regionview changes.
1016 * This method also sets up toggle action state as appropriate.
1019 Editor::sensitize_the_right_region_actions ()
1021 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1022 sensitize_all_region_actions (false);
1023 if (!selection->time.empty()) {
1024 _region_actions->get_action("split-region")->set_sensitive (true);
1028 } else if (mouse_mode != MouseObject) {
1029 sensitize_all_region_actions (false);
1033 /* We get here if we are in Object mode */
1035 RegionSelection rs = get_regions_from_selection_and_entered ();
1036 sensitize_all_region_actions (!rs.empty ());
1038 _ignore_region_action = true;
1040 /* Look through the regions that are selected and make notes about what we have got */
1042 bool have_audio = false;
1043 bool have_multichannel_audio = false;
1044 bool have_midi = false;
1045 bool have_locked = false;
1046 bool have_unlocked = false;
1047 bool have_position_lock_style_audio = false;
1048 bool have_position_lock_style_music = false;
1049 bool have_muted = false;
1050 bool have_unmuted = false;
1051 bool have_opaque = false;
1052 bool have_non_opaque = false;
1053 bool have_not_at_natural_position = false;
1054 bool have_envelope_active = false;
1055 bool have_envelope_inactive = false;
1056 bool have_non_unity_scale_amplitude = false;
1057 bool have_compound_regions = false;
1058 bool have_inactive_fade_in = false;
1059 bool have_inactive_fade_out = false;
1060 bool have_active_fade_in = false;
1061 bool have_active_fade_out = false;
1063 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1065 boost::shared_ptr<Region> r = (*i)->region ();
1066 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1070 if (ar->n_channels() > 1) {
1071 have_multichannel_audio = true;
1075 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1079 if (r->is_compound()) {
1080 have_compound_regions = true;
1086 have_unlocked = true;
1089 if (r->position_lock_style() == MusicTime) {
1090 have_position_lock_style_music = true;
1092 have_position_lock_style_audio = true;
1098 have_unmuted = true;
1104 have_non_opaque = true;
1107 if (!r->at_natural_position()) {
1108 have_not_at_natural_position = true;
1112 if (ar->envelope_active()) {
1113 have_envelope_active = true;
1115 have_envelope_inactive = true;
1118 if (ar->scale_amplitude() != 1) {
1119 have_non_unity_scale_amplitude = true;
1122 if (ar->fade_in_active ()) {
1123 have_active_fade_in = true;
1125 have_inactive_fade_in = true;
1128 if (ar->fade_out_active ()) {
1129 have_active_fade_out = true;
1131 have_inactive_fade_out = true;
1136 if (rs.size() > 1) {
1137 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1138 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1139 _region_actions->get_action("rename-region")->set_sensitive (false);
1141 _region_actions->get_action("combine-regions")->set_sensitive (true);
1143 _region_actions->get_action("combine-regions")->set_sensitive (false);
1145 } else if (rs.size() == 1) {
1146 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1147 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1148 _region_actions->get_action("combine-regions")->set_sensitive (false);
1151 if (!have_multichannel_audio) {
1152 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1156 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1157 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1158 _region_actions->get_action("quantize-region")->set_sensitive (false);
1159 _region_actions->get_action("fork-region")->set_sensitive (false);
1160 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1161 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1162 _region_actions->get_action("transpose-region")->set_sensitive (false);
1164 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1165 /* others were already marked sensitive */
1168 if (_edit_point == EditAtMouse) {
1169 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1170 _region_actions->get_action("trim-front")->set_sensitive (false);
1171 _region_actions->get_action("trim-back")->set_sensitive (false);
1172 _region_actions->get_action("split-region")->set_sensitive (false);
1173 _region_actions->get_action("place-transient")->set_sensitive (false);
1176 if (have_compound_regions) {
1177 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1179 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1184 if (have_envelope_active && !have_envelope_inactive) {
1185 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1186 } else if (have_envelope_active && have_envelope_inactive) {
1187 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1192 _region_actions->get_action("analyze-region")->set_sensitive (false);
1193 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1194 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1195 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1199 if (!have_non_unity_scale_amplitude || !have_audio) {
1200 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1203 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1204 a->set_active (have_locked && !have_unlocked);
1205 if (have_locked && have_unlocked) {
1206 // a->set_inconsistent ();
1209 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1210 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1212 if (have_position_lock_style_music && have_position_lock_style_audio) {
1213 // a->set_inconsistent ();
1216 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1217 a->set_active (have_muted && !have_unmuted);
1218 if (have_muted && have_unmuted) {
1219 // a->set_inconsistent ();
1222 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1223 a->set_active (have_opaque && !have_non_opaque);
1224 if (have_opaque && have_non_opaque) {
1225 // a->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 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1240 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1241 if (have_active_fade_in && have_inactive_fade_in) {
1242 // a->set_inconsistent ();
1245 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1246 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1248 if (have_active_fade_out && have_inactive_fade_out) {
1249 // a->set_inconsistent ();
1252 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1253 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1255 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1256 a->set_active (have_active_fade && !have_inactive_fade);
1258 if (have_active_fade && have_inactive_fade) {
1259 // a->set_inconsistent ();
1262 _ignore_region_action = false;
1264 _all_region_actions_sensitized = false;
1269 Editor::region_selection_changed ()
1271 _regions->block_change_connection (true);
1272 editor_regions_selection_changed_connection.block(true);
1274 if (_region_selection_change_updates_region_list) {
1275 _regions->unselect_all ();
1278 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1279 (*i)->set_selected_regionviews (selection->regions);
1282 if (_region_selection_change_updates_region_list) {
1283 _regions->set_selected (selection->regions);
1286 _regions->block_change_connection (false);
1287 editor_regions_selection_changed_connection.block(false);
1289 if (!_all_region_actions_sensitized) {
1290 /* This selection change might have changed what region actions
1291 are allowed, so sensitize them all in case a key is pressed.
1293 sensitize_all_region_actions (true);
1296 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1297 _session->request_locate (selection->regions.start());
1302 Editor::point_selection_changed ()
1304 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1305 (*i)->set_selected_points (selection->points);
1310 Editor::select_all_in_track (Selection::Operation op)
1312 list<Selectable *> touched;
1314 if (!clicked_routeview) {
1318 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1321 case Selection::Toggle:
1322 selection->add (touched);
1324 case Selection::Set:
1325 selection->set (touched);
1327 case Selection::Extend:
1328 /* meaningless, because we're selecting everything */
1330 case Selection::Add:
1331 selection->add (touched);
1337 Editor::select_all_internal_edit (Selection::Operation)
1339 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1340 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1342 mrv->select_all_notes ();
1348 Editor::select_all (Selection::Operation op)
1350 list<Selectable *> touched;
1352 if (_internal_editing) {
1353 select_all_internal_edit (op);
1357 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1358 if ((*iter)->hidden()) {
1361 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1363 begin_reversible_command (_("select all"));
1365 case Selection::Add:
1366 selection->add (touched);
1368 case Selection::Toggle:
1369 selection->add (touched);
1371 case Selection::Set:
1372 selection->set (touched);
1374 case Selection::Extend:
1375 /* meaningless, because we're selecting everything */
1378 commit_reversible_command ();
1382 Editor::invert_selection_in_track ()
1384 list<Selectable *> touched;
1386 if (!clicked_routeview) {
1390 clicked_routeview->get_inverted_selectables (*selection, touched);
1391 selection->set (touched);
1395 Editor::invert_selection ()
1397 list<Selectable *> touched;
1399 if (_internal_editing) {
1400 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1401 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1403 mrv->invert_selection ();
1409 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1410 if ((*iter)->hidden()) {
1413 (*iter)->get_inverted_selectables (*selection, touched);
1416 selection->set (touched);
1419 /** @param start Start time in session frames.
1420 * @param end End time in session frames.
1421 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1422 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1423 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1424 * within the region are already selected.
1427 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1429 list<Selectable*> found;
1431 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1433 if ((*iter)->hidden()) {
1437 (*iter)->get_selectables (start, end, top, bot, found);
1440 if (found.empty()) {
1444 if (preserve_if_selected && op != Selection::Toggle) {
1445 list<Selectable*>::iterator i = found.begin();
1446 while (i != found.end() && (*i)->get_selected()) {
1450 if (i == found.end()) {
1455 begin_reversible_command (_("select all within"));
1457 case Selection::Add:
1458 selection->add (found);
1460 case Selection::Toggle:
1461 selection->toggle (found);
1463 case Selection::Set:
1464 selection->set (found);
1466 case Selection::Extend:
1467 /* not defined yet */
1471 commit_reversible_command ();
1475 Editor::set_selection_from_region ()
1477 if (selection->regions.empty()) {
1481 selection->set (selection->regions.start(), selection->regions.end_frame());
1482 if (!Profile->get_sae()) {
1483 set_mouse_mode (Editing::MouseRange, false);
1488 Editor::set_selection_from_punch()
1492 if ((location = _session->locations()->auto_punch_location()) == 0) {
1496 set_selection_from_range (*location);
1500 Editor::set_selection_from_loop()
1504 if ((location = _session->locations()->auto_loop_location()) == 0) {
1507 set_selection_from_range (*location);
1511 Editor::set_selection_from_range (Location& loc)
1513 begin_reversible_command (_("set selection from range"));
1514 selection->set (loc.start(), loc.end());
1515 commit_reversible_command ();
1517 if (!Profile->get_sae()) {
1518 set_mouse_mode (Editing::MouseRange, false);
1523 Editor::select_all_selectables_using_time_selection ()
1525 list<Selectable *> touched;
1527 if (selection->time.empty()) {
1531 framepos_t start = selection->time[clicked_selection].start;
1532 framepos_t end = selection->time[clicked_selection].end;
1534 if (end - start < 1) {
1540 if (selection->tracks.empty()) {
1543 ts = &selection->tracks;
1546 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1547 if ((*iter)->hidden()) {
1550 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1553 begin_reversible_command (_("select all from range"));
1554 selection->set (touched);
1555 commit_reversible_command ();
1560 Editor::select_all_selectables_using_punch()
1562 Location* location = _session->locations()->auto_punch_location();
1563 list<Selectable *> touched;
1565 if (location == 0 || (location->end() - location->start() <= 1)) {
1572 if (selection->tracks.empty()) {
1575 ts = &selection->tracks;
1578 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1579 if ((*iter)->hidden()) {
1582 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1584 begin_reversible_command (_("select all from punch"));
1585 selection->set (touched);
1586 commit_reversible_command ();
1591 Editor::select_all_selectables_using_loop()
1593 Location* location = _session->locations()->auto_loop_location();
1594 list<Selectable *> touched;
1596 if (location == 0 || (location->end() - location->start() <= 1)) {
1603 if (selection->tracks.empty()) {
1606 ts = &selection->tracks;
1609 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1610 if ((*iter)->hidden()) {
1613 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1615 begin_reversible_command (_("select all from loop"));
1616 selection->set (touched);
1617 commit_reversible_command ();
1622 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1626 list<Selectable *> touched;
1629 start = cursor->current_frame;
1630 end = _session->current_end_frame();
1632 if (cursor->current_frame > 0) {
1634 end = cursor->current_frame - 1;
1640 if (_internal_editing) {
1641 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1642 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1644 mrv->select_range (start, end);
1651 begin_reversible_command (_("select all after cursor"));
1653 begin_reversible_command (_("select all before cursor"));
1658 if (selection->tracks.empty()) {
1661 ts = &selection->tracks;
1664 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1665 if ((*iter)->hidden()) {
1668 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1670 selection->set (touched);
1671 commit_reversible_command ();
1675 Editor::select_all_selectables_using_edit (bool after)
1679 list<Selectable *> touched;
1682 start = get_preferred_edit_position();
1683 end = _session->current_end_frame();
1685 if ((end = get_preferred_edit_position()) > 1) {
1693 if (_internal_editing) {
1694 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1695 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1696 mrv->select_range (start, end);
1702 begin_reversible_command (_("select all after edit"));
1704 begin_reversible_command (_("select all before edit"));
1709 if (selection->tracks.empty()) {
1712 ts = &selection->tracks;
1715 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1716 if ((*iter)->hidden()) {
1719 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1721 selection->set (touched);
1722 commit_reversible_command ();
1726 Editor::select_all_selectables_between (bool /*within*/)
1730 list<Selectable *> touched;
1732 if (!get_edit_op_range (start, end)) {
1736 if (_internal_editing) {
1737 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1738 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1739 mrv->select_range (start, end);
1746 if (selection->tracks.empty()) {
1749 ts = &selection->tracks;
1752 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1753 if ((*iter)->hidden()) {
1756 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1759 selection->set (touched);
1763 Editor::select_range_between ()
1768 if (mouse_mode == MouseRange && !selection->time.empty()) {
1769 selection->clear_time ();
1772 if (!get_edit_op_range (start, end)) {
1776 set_mouse_mode (MouseRange);
1777 selection->set (start, end);
1781 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1786 /* in range mode, use any existing selection */
1788 if (mouse_mode == MouseRange && !selection->time.empty()) {
1789 /* we know that these are ordered */
1790 start = selection->time.start();
1791 end = selection->time.end_frame();
1795 if (!mouse_frame (m, ignored)) {
1796 /* mouse is not in a canvas, try playhead+selected marker.
1797 this is probably most true when using menus.
1800 if (selection->markers.empty()) {
1804 start = selection->markers.front()->position();
1805 end = _session->audible_frame();
1809 switch (_edit_point) {
1810 case EditAtPlayhead:
1811 if (selection->markers.empty()) {
1812 /* use mouse + playhead */
1814 end = _session->audible_frame();
1816 /* use playhead + selected marker */
1817 start = _session->audible_frame();
1818 end = selection->markers.front()->position();
1823 /* use mouse + selected marker */
1824 if (selection->markers.empty()) {
1826 end = _session->audible_frame();
1828 start = selection->markers.front()->position();
1833 case EditAtSelectedMarker:
1834 /* use mouse + selected marker */
1835 if (selection->markers.empty()) {
1837 MessageDialog win (_("No edit range defined"),
1842 win.set_secondary_text (
1843 _("the edit point is Selected Marker\nbut there is no selected marker."));
1846 win.set_default_response (RESPONSE_CLOSE);
1847 win.set_position (Gtk::WIN_POS_MOUSE);
1852 return false; // NO RANGE
1854 start = selection->markers.front()->position();
1868 /* turn range into one delimited by start...end,
1878 Editor::deselect_all ()
1880 selection->clear ();
1884 Editor::select_range (framepos_t s, framepos_t e)
1886 selection->add (clicked_axisview);
1887 selection->time.clear ();
1888 return selection->set (s, e);