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 (Selection::Operation op, bool /*no_remove*/)
319 if (!clicked_control_point) {
325 selection->set (clicked_control_point);
328 selection->add (clicked_control_point);
330 case Selection::Toggle:
331 selection->toggle (clicked_control_point);
333 case Selection::Extend:
342 Editor::get_onscreen_tracks (TrackViewList& tvl)
344 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
345 if ((*i)->y_position() < _canvas_height) {
351 /** Call a slot for a given `basis' track and also for any track that is in the same
352 * active route group with a particular set of properties.
354 * @param sl Slot to call.
355 * @param basis Basis track.
356 * @param prop Properties that active edit groups must share to be included in the map.
360 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
362 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
364 if (route_basis == 0) {
368 set<RouteTimeAxisView*> tracks;
369 tracks.insert (route_basis);
371 RouteGroup* group = route_basis->route()->route_group();
373 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
375 /* the basis is a member of an active route group, with the appropriate
376 properties; find other members */
378 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
379 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
380 if (v && v->route()->route_group() == group) {
387 uint32_t const sz = tracks.size ();
389 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
394 /** Call a slot for a given `basis' track and also for any track that is in the same
395 * active route group with a particular set of properties.
397 * @param sl Slot to call.
398 * @param basis Basis track.
399 * @param prop Properties that active edit groups must share to be included in the map.
403 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
405 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
406 set<boost::shared_ptr<Playlist> > playlists;
408 if (route_basis == 0) {
412 set<RouteTimeAxisView*> tracks;
413 tracks.insert (route_basis);
415 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
417 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
419 /* the basis is a member of an active route group, with the appropriate
420 properties; find other members */
422 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
423 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
425 if (v && v->route()->route_group() == group) {
427 boost::shared_ptr<Track> t = v->track();
429 if (playlists.insert (t->playlist()).second) {
430 /* haven't seen this playlist yet */
434 /* not actually a "Track", but a timeaxis view that
435 we should mapover anyway.
444 uint32_t const sz = tracks.size ();
446 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
452 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
454 boost::shared_ptr<Playlist> pl;
455 vector<boost::shared_ptr<Region> > results;
457 boost::shared_ptr<Track> tr;
459 if ((tr = tv.track()) == 0) {
464 if (&tv == &basis->get_time_axis_view()) {
465 /* looking in same track as the original */
469 if ((pl = tr->playlist()) != 0) {
470 pl->get_equivalent_regions (basis->region(), results);
473 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
474 if ((marv = tv.view()->find_view (*ir)) != 0) {
475 all_equivs->push_back (marv);
481 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
483 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);
485 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
487 equivalent_regions.push_back (basis);
491 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
493 RegionSelection equivalent;
495 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
497 vector<RegionView*> eq;
499 mapover_tracks_with_unique_playlists (
500 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
501 &(*i)->get_time_axis_view(), prop);
503 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
514 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
516 int region_count = 0;
518 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
520 RouteTimeAxisView* tatv;
522 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
524 boost::shared_ptr<Playlist> pl;
525 vector<boost::shared_ptr<Region> > results;
527 boost::shared_ptr<Track> tr;
529 if ((tr = tatv->track()) == 0) {
534 if ((pl = (tr->playlist())) != 0) {
535 pl->get_region_list_equivalent_regions (region, results);
538 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
539 if ((marv = tatv->view()->find_view (*ir)) != 0) {
552 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
554 vector<RegionView*> all_equivalent_regions;
557 if (!clicked_regionview || !clicked_routeview) {
562 button_release_can_deselect = false;
565 if (op == Selection::Toggle || op == Selection::Set) {
568 case Selection::Toggle:
569 if (selection->selected (clicked_regionview)) {
572 /* whatever was clicked was selected already; do nothing here but allow
573 the button release to deselect it
576 button_release_can_deselect = true;
579 if (button_release_can_deselect) {
581 /* just remove this one region, but only on a permitted button release */
583 selection->remove (clicked_regionview);
586 /* no more deselect action on button release till a new press
587 finds an already selected object.
590 button_release_can_deselect = false;
598 if (selection->selected (clicked_routeview)) {
599 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
601 all_equivalent_regions.push_back (clicked_regionview);
604 /* add all the equivalent regions, but only on button press */
606 if (!all_equivalent_regions.empty()) {
610 selection->add (all_equivalent_regions);
616 if (!selection->selected (clicked_regionview)) {
617 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
618 selection->set (all_equivalent_regions);
621 /* no commit necessary: clicked on an already selected region */
631 } else if (op == Selection::Extend) {
633 list<Selectable*> results;
634 framepos_t last_frame;
635 framepos_t first_frame;
636 bool same_track = false;
638 /* 1. find the last selected regionview in the track that was clicked in */
641 first_frame = max_framepos;
643 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
644 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
646 if ((*x)->region()->last_frame() > last_frame) {
647 last_frame = (*x)->region()->last_frame();
650 if ((*x)->region()->first_frame() < first_frame) {
651 first_frame = (*x)->region()->first_frame();
660 /* 2. figure out the boundaries for our search for new objects */
662 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
663 case Evoral::OverlapNone:
664 if (last_frame < clicked_regionview->region()->first_frame()) {
665 first_frame = last_frame;
666 last_frame = clicked_regionview->region()->last_frame();
668 last_frame = first_frame;
669 first_frame = clicked_regionview->region()->first_frame();
673 case Evoral::OverlapExternal:
674 if (last_frame < clicked_regionview->region()->first_frame()) {
675 first_frame = last_frame;
676 last_frame = clicked_regionview->region()->last_frame();
678 last_frame = first_frame;
679 first_frame = clicked_regionview->region()->first_frame();
683 case Evoral::OverlapInternal:
684 if (last_frame < clicked_regionview->region()->first_frame()) {
685 first_frame = last_frame;
686 last_frame = clicked_regionview->region()->last_frame();
688 last_frame = first_frame;
689 first_frame = clicked_regionview->region()->first_frame();
693 case Evoral::OverlapStart:
694 case Evoral::OverlapEnd:
695 /* nothing to do except add clicked region to selection, since it
696 overlaps with the existing selection in this track.
703 /* click in a track that has no regions selected, so extend vertically
704 to pick out all regions that are defined by the existing selection
709 first_frame = clicked_regionview->region()->position();
710 last_frame = clicked_regionview->region()->last_frame();
712 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
713 if ((*i)->region()->position() < first_frame) {
714 first_frame = (*i)->region()->position();
716 if ((*i)->region()->last_frame() + 1 > last_frame) {
717 last_frame = (*i)->region()->last_frame();
722 /* 2. find all the tracks we should select in */
724 set<RouteTimeAxisView*> relevant_tracks;
726 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
727 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
729 relevant_tracks.insert (r);
733 set<RouteTimeAxisView*> already_in_selection;
735 if (relevant_tracks.empty()) {
737 /* no tracks selected .. thus .. if the
738 regionview we're in isn't selected
739 (i.e. we're about to extend to it), then
740 find all tracks between the this one and
744 if (!selection->selected (clicked_regionview)) {
746 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
750 /* add this track to the ones we will search */
752 relevant_tracks.insert (rtv);
754 /* find the track closest to this one that
755 already a selected region.
758 RouteTimeAxisView* closest = 0;
759 int distance = INT_MAX;
760 int key = rtv->route()->order_key ("editor");
762 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
764 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
766 if (artv && artv != rtv) {
768 pair<set<RouteTimeAxisView*>::iterator,bool> result;
770 result = already_in_selection.insert (artv);
773 /* newly added to already_in_selection */
775 int d = artv->route()->order_key ("editor");
779 if (abs (d) < distance) {
789 /* now add all tracks between that one and this one */
791 int okey = closest->route()->order_key ("editor");
797 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
798 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
799 if (artv && artv != rtv) {
801 int k = artv->route()->order_key ("editor");
803 if (k >= okey && k <= key) {
805 /* in range but don't add it if
806 it already has tracks selected.
807 this avoids odd selection
808 behaviour that feels wrong.
811 if (find (already_in_selection.begin(),
812 already_in_selection.end(),
813 artv) == already_in_selection.end()) {
815 relevant_tracks.insert (artv);
825 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
826 one that was clicked.
829 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
830 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
833 /* 4. convert to a vector of regions */
835 vector<RegionView*> regions;
837 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
840 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
841 regions.push_back (arv);
845 if (!regions.empty()) {
846 selection->add (regions);
857 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
859 vector<RegionView*> all_equivalent_regions;
861 get_regions_corresponding_to (region, all_equivalent_regions);
863 if (all_equivalent_regions.empty()) {
867 begin_reversible_command (_("set selected regions"));
870 case Selection::Toggle:
871 /* XXX this is not correct */
872 selection->toggle (all_equivalent_regions);
875 selection->set (all_equivalent_regions);
877 case Selection::Extend:
878 selection->add (all_equivalent_regions);
881 selection->add (all_equivalent_regions);
885 commit_reversible_command () ;
889 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
892 boost::shared_ptr<Region> r (weak_r.lock());
898 if ((rv = sv->find_view (r)) == 0) {
902 /* don't reset the selection if its something other than
903 a single other region.
906 if (selection->regions.size() > 1) {
910 begin_reversible_command (_("set selected regions"));
914 commit_reversible_command () ;
920 Editor::track_selection_changed ()
922 switch (selection->tracks.size()) {
926 set_selected_mixer_strip (*(selection->tracks.front()));
930 RouteNotificationListPtr routes (new RouteNotificationList);
932 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
934 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
936 (*i)->set_selected (yn);
938 TimeAxisView::Children c = (*i)->get_child_list ();
939 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
940 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
944 ((mouse_mode == MouseRange) ||
945 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
946 (*i)->reshow_selection (selection->time);
948 (*i)->hide_selection ();
953 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
955 routes->push_back (rtav->route());
960 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
962 /* notify control protocols */
964 ControlProtocol::TrackSelectionChanged (routes);
968 Editor::time_selection_changed ()
970 if (Profile->get_sae()) {
974 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
975 (*i)->hide_selection ();
978 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
979 (*i)->show_selection (selection->time);
982 if (selection->time.empty()) {
983 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
985 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
988 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
989 _session->request_locate (selection->time.start());
993 /** Set all region actions to have a given sensitivity */
995 Editor::sensitize_all_region_actions (bool s)
997 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
999 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1000 (*i)->set_sensitive (s);
1003 _all_region_actions_sensitized = s;
1006 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1007 * This method should be called just before displaying a Region menu. When a Region menu is not
1008 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1009 * on entered_regionviews work without having to check sensitivity every time the selection or
1010 * entered_regionview changes.
1012 * This method also sets up toggle action state as appropriate.
1015 Editor::sensitize_the_right_region_actions ()
1017 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1018 sensitize_all_region_actions (false);
1019 if (!selection->time.empty()) {
1020 _region_actions->get_action("split-region")->set_sensitive (true);
1024 } else if (mouse_mode != MouseObject) {
1025 sensitize_all_region_actions (false);
1029 /* We get here if we are in Object mode */
1031 RegionSelection rs = get_regions_from_selection_and_entered ();
1032 sensitize_all_region_actions (!rs.empty ());
1034 _ignore_region_action = true;
1036 /* Look through the regions that are selected and make notes about what we have got */
1038 bool have_audio = false;
1039 bool have_multichannel_audio = false;
1040 bool have_midi = false;
1041 bool have_locked = false;
1042 bool have_unlocked = false;
1043 bool have_position_lock_style_audio = false;
1044 bool have_position_lock_style_music = false;
1045 bool have_muted = false;
1046 bool have_unmuted = false;
1047 bool have_opaque = false;
1048 bool have_non_opaque = false;
1049 bool have_not_at_natural_position = false;
1050 bool have_envelope_active = false;
1051 bool have_envelope_inactive = false;
1052 bool have_non_unity_scale_amplitude = false;
1053 bool have_compound_regions = false;
1055 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1057 boost::shared_ptr<Region> r = (*i)->region ();
1058 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1062 if (ar->n_channels() > 1) {
1063 have_multichannel_audio = true;
1067 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1071 if (r->is_compound()) {
1072 have_compound_regions = true;
1078 have_unlocked = true;
1081 if (r->position_lock_style() == MusicTime) {
1082 have_position_lock_style_music = true;
1084 have_position_lock_style_audio = true;
1090 have_unmuted = true;
1096 have_non_opaque = true;
1099 if (!r->at_natural_position()) {
1100 have_not_at_natural_position = true;
1104 if (ar->envelope_active()) {
1105 have_envelope_active = true;
1107 have_envelope_inactive = true;
1110 if (ar->scale_amplitude() != 1) {
1111 have_non_unity_scale_amplitude = true;
1116 if (rs.size() > 1) {
1117 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1118 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1119 _region_actions->get_action("rename-region")->set_sensitive (false);
1121 _region_actions->get_action("combine-regions")->set_sensitive (true);
1123 _region_actions->get_action("combine-regions")->set_sensitive (false);
1125 } else if (rs.size() == 1) {
1126 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1127 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1128 _region_actions->get_action("combine-regions")->set_sensitive (false);
1131 if (!have_multichannel_audio) {
1132 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1136 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1137 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1138 _region_actions->get_action("quantize-region")->set_sensitive (false);
1139 _region_actions->get_action("fork-region")->set_sensitive (false);
1140 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1141 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1142 _region_actions->get_action("transpose-region")->set_sensitive (false);
1144 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1145 /* others were already marked sensitive */
1148 if (_edit_point == EditAtMouse) {
1149 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1150 _region_actions->get_action("trim-front")->set_sensitive (false);
1151 _region_actions->get_action("trim-back")->set_sensitive (false);
1152 _region_actions->get_action("split-region")->set_sensitive (false);
1153 _region_actions->get_action("place-transient")->set_sensitive (false);
1156 if (have_compound_regions) {
1157 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1159 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1164 if (have_envelope_active && !have_envelope_inactive) {
1165 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1166 } else if (have_envelope_active && have_envelope_inactive) {
1167 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1172 _region_actions->get_action("analyze-region")->set_sensitive (false);
1173 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1174 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1175 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1179 if (!have_non_unity_scale_amplitude || !have_audio) {
1180 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1183 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1184 if (have_locked && have_unlocked) {
1185 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1188 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);
1190 if (have_position_lock_style_music && have_position_lock_style_audio) {
1191 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1194 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1195 if (have_muted && have_unmuted) {
1196 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1199 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1200 if (have_opaque && have_non_opaque) {
1201 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1204 if (!have_not_at_natural_position) {
1205 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1208 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1209 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1210 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1212 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1215 _ignore_region_action = false;
1217 _all_region_actions_sensitized = false;
1222 Editor::region_selection_changed ()
1224 _regions->block_change_connection (true);
1225 editor_regions_selection_changed_connection.block(true);
1227 if (_region_selection_change_updates_region_list) {
1228 _regions->unselect_all ();
1231 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1232 (*i)->set_selected_regionviews (selection->regions);
1235 if (_region_selection_change_updates_region_list) {
1236 _regions->set_selected (selection->regions);
1239 _regions->block_change_connection (false);
1240 editor_regions_selection_changed_connection.block(false);
1242 if (!_all_region_actions_sensitized) {
1243 /* This selection change might have changed what region actions
1244 are allowed, so sensitize them all in case a key is pressed.
1246 sensitize_all_region_actions (true);
1249 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1250 _session->request_locate (selection->regions.start());
1255 Editor::point_selection_changed ()
1257 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1258 (*i)->set_selected_points (selection->points);
1263 Editor::select_all_in_track (Selection::Operation op)
1265 list<Selectable *> touched;
1267 if (!clicked_routeview) {
1271 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1274 case Selection::Toggle:
1275 selection->add (touched);
1277 case Selection::Set:
1278 selection->set (touched);
1280 case Selection::Extend:
1281 /* meaningless, because we're selecting everything */
1283 case Selection::Add:
1284 selection->add (touched);
1290 Editor::select_all_internal_edit (Selection::Operation)
1292 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1293 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1295 mrv->select_all_notes ();
1301 Editor::select_all (Selection::Operation op)
1303 list<Selectable *> touched;
1305 if (_internal_editing) {
1306 select_all_internal_edit (op);
1310 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1311 if ((*iter)->hidden()) {
1314 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1316 begin_reversible_command (_("select all"));
1318 case Selection::Add:
1319 selection->add (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 */
1331 commit_reversible_command ();
1335 Editor::invert_selection_in_track ()
1337 list<Selectable *> touched;
1339 if (!clicked_routeview) {
1343 clicked_routeview->get_inverted_selectables (*selection, touched);
1344 selection->set (touched);
1348 Editor::invert_selection ()
1350 list<Selectable *> touched;
1352 if (_internal_editing) {
1353 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1354 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1356 mrv->invert_selection ();
1362 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1363 if ((*iter)->hidden()) {
1366 (*iter)->get_inverted_selectables (*selection, touched);
1369 selection->set (touched);
1372 /** @param start Start time in session frames.
1373 * @param end End time in session frames.
1374 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1375 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1376 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1377 * within the region are already selected.
1380 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1382 list<Selectable*> found;
1384 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1386 if ((*iter)->hidden()) {
1390 (*iter)->get_selectables (start, end, top, bot, found);
1393 if (found.empty()) {
1397 if (preserve_if_selected && op != Selection::Toggle) {
1398 list<Selectable*>::iterator i = found.begin();
1399 while (i != found.end() && (*i)->get_selected()) {
1403 if (i == found.end()) {
1408 begin_reversible_command (_("select all within"));
1410 case Selection::Add:
1411 selection->add (found);
1413 case Selection::Toggle:
1414 selection->toggle (found);
1416 case Selection::Set:
1417 selection->set (found);
1419 case Selection::Extend:
1420 /* not defined yet */
1424 commit_reversible_command ();
1428 Editor::set_selection_from_region ()
1430 if (selection->regions.empty()) {
1434 selection->set (selection->regions.start(), selection->regions.end_frame());
1435 if (!Profile->get_sae()) {
1436 set_mouse_mode (Editing::MouseRange, false);
1441 Editor::set_selection_from_punch()
1445 if ((location = _session->locations()->auto_punch_location()) == 0) {
1449 set_selection_from_range (*location);
1453 Editor::set_selection_from_loop()
1457 if ((location = _session->locations()->auto_loop_location()) == 0) {
1460 set_selection_from_range (*location);
1464 Editor::set_selection_from_range (Location& loc)
1466 begin_reversible_command (_("set selection from range"));
1467 selection->set (loc.start(), loc.end());
1468 commit_reversible_command ();
1470 if (!Profile->get_sae()) {
1471 set_mouse_mode (Editing::MouseRange, false);
1476 Editor::select_all_selectables_using_time_selection ()
1478 list<Selectable *> touched;
1480 if (selection->time.empty()) {
1484 framepos_t start = selection->time[clicked_selection].start;
1485 framepos_t end = selection->time[clicked_selection].end;
1487 if (end - start < 1) {
1493 if (selection->tracks.empty()) {
1496 ts = &selection->tracks;
1499 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1500 if ((*iter)->hidden()) {
1503 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1506 begin_reversible_command (_("select all from range"));
1507 selection->set (touched);
1508 commit_reversible_command ();
1513 Editor::select_all_selectables_using_punch()
1515 Location* location = _session->locations()->auto_punch_location();
1516 list<Selectable *> touched;
1518 if (location == 0 || (location->end() - location->start() <= 1)) {
1525 if (selection->tracks.empty()) {
1528 ts = &selection->tracks;
1531 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1532 if ((*iter)->hidden()) {
1535 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1537 begin_reversible_command (_("select all from punch"));
1538 selection->set (touched);
1539 commit_reversible_command ();
1544 Editor::select_all_selectables_using_loop()
1546 Location* location = _session->locations()->auto_loop_location();
1547 list<Selectable *> touched;
1549 if (location == 0 || (location->end() - location->start() <= 1)) {
1556 if (selection->tracks.empty()) {
1559 ts = &selection->tracks;
1562 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1563 if ((*iter)->hidden()) {
1566 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1568 begin_reversible_command (_("select all from loop"));
1569 selection->set (touched);
1570 commit_reversible_command ();
1575 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1579 list<Selectable *> touched;
1582 start = cursor->current_frame;
1583 end = _session->current_end_frame();
1585 if (cursor->current_frame > 0) {
1587 end = cursor->current_frame - 1;
1593 if (_internal_editing) {
1594 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1595 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1597 mrv->select_range (start, end);
1604 begin_reversible_command (_("select all after cursor"));
1606 begin_reversible_command (_("select all before cursor"));
1611 if (selection->tracks.empty()) {
1614 ts = &selection->tracks;
1617 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1618 if ((*iter)->hidden()) {
1621 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1623 selection->set (touched);
1624 commit_reversible_command ();
1628 Editor::select_all_selectables_using_edit (bool after)
1632 list<Selectable *> touched;
1635 start = get_preferred_edit_position();
1636 end = _session->current_end_frame();
1638 if ((end = get_preferred_edit_position()) > 1) {
1646 if (_internal_editing) {
1647 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1648 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1649 mrv->select_range (start, end);
1655 begin_reversible_command (_("select all after edit"));
1657 begin_reversible_command (_("select all before edit"));
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_between (bool /*within*/)
1683 list<Selectable *> touched;
1685 if (!get_edit_op_range (start, end)) {
1689 if (_internal_editing) {
1690 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1691 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1692 mrv->select_range (start, end);
1699 if (selection->tracks.empty()) {
1702 ts = &selection->tracks;
1705 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1706 if ((*iter)->hidden()) {
1709 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1712 selection->set (touched);
1716 Editor::select_range_between ()
1721 if (mouse_mode == MouseRange && !selection->time.empty()) {
1722 selection->clear_time ();
1725 if (!get_edit_op_range (start, end)) {
1729 set_mouse_mode (MouseRange);
1730 selection->set (start, end);
1734 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1739 /* in range mode, use any existing selection */
1741 if (mouse_mode == MouseRange && !selection->time.empty()) {
1742 /* we know that these are ordered */
1743 start = selection->time.start();
1744 end = selection->time.end_frame();
1748 if (!mouse_frame (m, ignored)) {
1749 /* mouse is not in a canvas, try playhead+selected marker.
1750 this is probably most true when using menus.
1753 if (selection->markers.empty()) {
1757 start = selection->markers.front()->position();
1758 end = _session->audible_frame();
1762 switch (_edit_point) {
1763 case EditAtPlayhead:
1764 if (selection->markers.empty()) {
1765 /* use mouse + playhead */
1767 end = _session->audible_frame();
1769 /* use playhead + selected marker */
1770 start = _session->audible_frame();
1771 end = selection->markers.front()->position();
1776 /* use mouse + selected marker */
1777 if (selection->markers.empty()) {
1779 end = _session->audible_frame();
1781 start = selection->markers.front()->position();
1786 case EditAtSelectedMarker:
1787 /* use mouse + selected marker */
1788 if (selection->markers.empty()) {
1790 MessageDialog win (_("No edit range defined"),
1795 win.set_secondary_text (
1796 _("the edit point is Selected Marker\nbut there is no selected marker."));
1799 win.set_default_response (RESPONSE_CLOSE);
1800 win.set_position (Gtk::WIN_POS_MOUSE);
1805 return false; // NO RANGE
1807 start = selection->markers.front()->position();
1821 /* turn range into one delimited by start...end,
1831 Editor::deselect_all ()
1833 selection->clear ();
1837 Editor::select_range (framepos_t s, framepos_t e)
1839 selection->add (clicked_axisview);
1840 selection->time.clear ();
1841 return selection->set (s, e);