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"
33 #include "audio_time_axis.h"
34 #include "audio_region_view.h"
35 #include "audio_streamview.h"
36 #include "automation_line.h"
37 #include "control_point.h"
38 #include "editor_regions.h"
39 #include "editor_cursors.h"
40 #include "midi_region_view.h"
45 using namespace ARDOUR;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
52 struct TrackViewByPositionSorter
54 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
55 return a->y_position() < b->y_position();
60 Editor::extend_selection_to_track (TimeAxisView& view)
62 if (selection->selected (&view)) {
63 /* already selected, do nothing */
67 if (selection->tracks.empty()) {
69 if (!selection->selected (&view)) {
70 selection->set (&view);
77 /* something is already selected, so figure out which range of things to add */
79 TrackViewList to_be_added;
80 TrackViewList sorted = track_views;
81 TrackViewByPositionSorter cmp;
82 bool passed_clicked = false;
87 if (!selection->selected (&view)) {
88 to_be_added.push_back (&view);
91 /* figure out if we should go forward or backwards */
93 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
96 passed_clicked = true;
99 if (selection->selected (*i)) {
100 if (passed_clicked) {
109 passed_clicked = false;
113 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
116 passed_clicked = true;
120 if (passed_clicked) {
121 if ((*i)->hidden()) {
124 if (selection->selected (*i)) {
126 } else if (!(*i)->hidden()) {
127 to_be_added.push_back (*i);
134 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
137 passed_clicked = true;
141 if (passed_clicked) {
143 if ((*r)->hidden()) {
147 if (selection->selected (*r)) {
149 } else if (!(*r)->hidden()) {
150 to_be_added.push_back (*r);
156 if (!to_be_added.empty()) {
157 selection->add (to_be_added);
165 Editor::select_all_tracks ()
167 TrackViewList visible_views;
168 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
169 if ((*i)->marked_for_display()) {
170 visible_views.push_back (*i);
173 selection->set (visible_views);
176 /** Select clicked_axisview, unless there are no currently selected
177 * tracks, in which case nothing will happen unless `force' is true.
180 Editor::set_selected_track_as_side_effect (Selection::Operation op)
182 if (!clicked_axisview) {
186 if (!clicked_routeview) {
190 bool had_tracks = !selection->tracks.empty();
191 RouteGroup* group = clicked_routeview->route()->route_group();
192 RouteGroup& arg (_session->all_route_group());
195 case Selection::Toggle:
196 if (selection->selected (clicked_axisview)) {
197 if (arg.is_select() && arg.is_active()) {
198 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
199 selection->remove(*i);
201 } else if (group && group->is_active()) {
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203 if ((*i)->route_group() == group)
204 selection->remove(*i);
207 selection->remove (clicked_axisview);
210 if (arg.is_select() && arg.is_active()) {
211 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
214 } else if (group && group->is_active()) {
215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216 if ( (*i)->route_group() == group)
220 selection->add (clicked_axisview);
226 if (!had_tracks && arg.is_select() && arg.is_active()) {
227 /* nothing was selected already, and all group is active etc. so use
230 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
233 } else if (group && group->is_active()) {
234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
235 if ((*i)->route_group() == group)
239 selection->add (clicked_axisview);
245 if (!had_tracks && arg.is_select() && arg.is_active()) {
246 /* nothing was selected already, and all group is active etc. so use
249 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
252 } else if (group && group->is_active()) {
253 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
254 if ((*i)->route_group() == group)
258 selection->set (clicked_axisview);
262 case Selection::Extend:
264 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
270 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
273 case Selection::Toggle:
274 if (selection->selected (&view)) {
276 selection->remove (&view);
279 selection->add (&view);
284 if (!selection->selected (&view)) {
285 selection->add (&view);
290 selection->set (&view);
293 case Selection::Extend:
294 extend_selection_to_track (view);
300 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
302 if (!clicked_routeview) {
310 set_selected_track (*clicked_routeview, op, no_remove);
314 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
316 if (!clicked_control_point) {
322 selection->set (clicked_control_point);
325 selection->add (clicked_control_point);
327 case Selection::Toggle:
328 selection->toggle (clicked_control_point);
330 case Selection::Extend:
339 Editor::get_onscreen_tracks (TrackViewList& tvl)
341 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
342 if ((*i)->y_position() < _canvas_height) {
348 /** Call a slot for a given `basis' track and also for any track that is in the same
349 * active route group with a particular set of properties.
351 * @param sl Slot to call.
352 * @param basis Basis track.
353 * @param prop Properties that active edit groups must share to be included in the map.
357 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
359 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
361 if (route_basis == 0) {
365 set<RouteTimeAxisView*> tracks;
366 tracks.insert (route_basis);
368 RouteGroup* group = route_basis->route()->route_group();
370 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
372 /* the basis is a member of an active route group, with the appropriate
373 properties; find other members */
375 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
376 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
377 if (v && v->route()->route_group() == group) {
384 uint32_t const sz = tracks.size ();
386 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
391 /** Call a slot for a given `basis' track and also for any track that is in the same
392 * active route group with a particular set of properties.
394 * @param sl Slot to call.
395 * @param basis Basis track.
396 * @param prop Properties that active edit groups must share to be included in the map.
400 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
402 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
403 set<boost::shared_ptr<Playlist> > playlists;
405 if (route_basis == 0) {
409 set<RouteTimeAxisView*> tracks;
410 tracks.insert (route_basis);
412 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
414 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
416 /* the basis is a member of an active route group, with the appropriate
417 properties; find other members */
419 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
420 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
422 if (v && v->route()->route_group() == group) {
424 boost::shared_ptr<Track> t = v->track();
426 if (playlists.insert (t->playlist()).second) {
427 /* haven't seen this playlist yet */
431 /* not actually a "Track", but a timeaxis view that
432 we should mapover anyway.
441 uint32_t const sz = tracks.size ();
443 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
449 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
451 boost::shared_ptr<Playlist> pl;
452 vector<boost::shared_ptr<Region> > results;
454 boost::shared_ptr<Track> tr;
456 if ((tr = tv.track()) == 0) {
461 if (&tv == &basis->get_time_axis_view()) {
462 /* looking in same track as the original */
466 if ((pl = tr->playlist()) != 0) {
467 pl->get_equivalent_regions (basis->region(), results);
470 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
471 if ((marv = tv.view()->find_view (*ir)) != 0) {
472 all_equivs->push_back (marv);
478 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
480 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);
482 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
484 equivalent_regions.push_back (basis);
488 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
490 RegionSelection equivalent;
492 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
494 vector<RegionView*> eq;
496 mapover_tracks_with_unique_playlists (
497 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
498 &(*i)->get_time_axis_view(), prop);
500 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
512 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
514 int region_count = 0;
516 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
518 RouteTimeAxisView* tatv;
520 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
522 boost::shared_ptr<Playlist> pl;
523 vector<boost::shared_ptr<Region> > results;
525 boost::shared_ptr<Track> tr;
527 if ((tr = tatv->track()) == 0) {
532 if ((pl = (tr->playlist())) != 0) {
533 pl->get_region_list_equivalent_regions (region, results);
536 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
537 if ((marv = tatv->view()->find_view (*ir)) != 0) {
550 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
552 vector<RegionView*> all_equivalent_regions;
555 if (!clicked_regionview || !clicked_routeview) {
560 button_release_can_deselect = false;
563 if (op == Selection::Toggle || op == Selection::Set) {
566 case Selection::Toggle:
567 if (selection->selected (clicked_regionview)) {
570 /* whatever was clicked was selected already; do nothing here but allow
571 the button release to deselect it
574 button_release_can_deselect = true;
577 if (button_release_can_deselect) {
579 /* just remove this one region, but only on a permitted button release */
581 selection->remove (clicked_regionview);
584 /* no more deselect action on button release till a new press
585 finds an already selected object.
588 button_release_can_deselect = false;
596 if (selection->selected (clicked_routeview)) {
597 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
599 all_equivalent_regions.push_back (clicked_regionview);
602 /* add all the equivalent regions, but only on button press */
604 if (!all_equivalent_regions.empty()) {
608 selection->add (all_equivalent_regions);
614 if (!selection->selected (clicked_regionview)) {
615 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
616 selection->set (all_equivalent_regions);
619 /* no commit necessary: clicked on an already selected region */
629 } else if (op == Selection::Extend) {
631 list<Selectable*> results;
632 framepos_t last_frame;
633 framepos_t first_frame;
634 bool same_track = false;
636 /* 1. find the last selected regionview in the track that was clicked in */
639 first_frame = max_framepos;
641 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
642 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
644 if ((*x)->region()->last_frame() > last_frame) {
645 last_frame = (*x)->region()->last_frame();
648 if ((*x)->region()->first_frame() < first_frame) {
649 first_frame = (*x)->region()->first_frame();
658 /* 2. figure out the boundaries for our search for new objects */
660 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
662 if (last_frame < clicked_regionview->region()->first_frame()) {
663 first_frame = last_frame;
664 last_frame = clicked_regionview->region()->last_frame();
666 last_frame = first_frame;
667 first_frame = clicked_regionview->region()->first_frame();
671 case OverlapExternal:
672 if (last_frame < clicked_regionview->region()->first_frame()) {
673 first_frame = last_frame;
674 last_frame = clicked_regionview->region()->last_frame();
676 last_frame = first_frame;
677 first_frame = clicked_regionview->region()->first_frame();
681 case OverlapInternal:
682 if (last_frame < clicked_regionview->region()->first_frame()) {
683 first_frame = last_frame;
684 last_frame = clicked_regionview->region()->last_frame();
686 last_frame = first_frame;
687 first_frame = clicked_regionview->region()->first_frame();
693 /* nothing to do except add clicked region to selection, since it
694 overlaps with the existing selection in this track.
701 /* click in a track that has no regions selected, so extend vertically
702 to pick out all regions that are defined by the existing selection
707 first_frame = clicked_regionview->region()->position();
708 last_frame = clicked_regionview->region()->last_frame();
710 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
711 if ((*i)->region()->position() < first_frame) {
712 first_frame = (*i)->region()->position();
714 if ((*i)->region()->last_frame() + 1 > last_frame) {
715 last_frame = (*i)->region()->last_frame();
720 /* 2. find all the tracks we should select in */
722 set<RouteTimeAxisView*> relevant_tracks;
724 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
725 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
727 relevant_tracks.insert (r);
731 set<RouteTimeAxisView*> already_in_selection;
733 if (relevant_tracks.empty()) {
735 /* no tracks selected .. thus .. if the
736 regionview we're in isn't selected
737 (i.e. we're about to extend to it), then
738 find all tracks between the this one and
742 if (!selection->selected (clicked_regionview)) {
744 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
748 /* add this track to the ones we will search */
750 relevant_tracks.insert (rtv);
752 /* find the track closest to this one that
753 already a selected region.
756 RouteTimeAxisView* closest = 0;
757 int distance = INT_MAX;
758 int key = rtv->route()->order_key ("editor");
760 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
762 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
764 if (artv && artv != rtv) {
766 pair<set<RouteTimeAxisView*>::iterator,bool> result;
768 result = already_in_selection.insert (artv);
771 /* newly added to already_in_selection */
773 int d = artv->route()->order_key ("editor");
777 if (abs (d) < distance) {
787 /* now add all tracks between that one and this one */
789 int okey = closest->route()->order_key ("editor");
795 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
796 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
797 if (artv && artv != rtv) {
799 int k = artv->route()->order_key ("editor");
801 if (k >= okey && k <= key) {
803 /* in range but don't add it if
804 it already has tracks selected.
805 this avoids odd selection
806 behaviour that feels wrong.
809 if (find (already_in_selection.begin(),
810 already_in_selection.end(),
811 artv) == already_in_selection.end()) {
813 relevant_tracks.insert (artv);
823 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
824 one that was clicked.
827 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
828 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
831 /* 4. convert to a vector of regions */
833 vector<RegionView*> regions;
835 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
838 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
839 regions.push_back (arv);
843 if (!regions.empty()) {
844 selection->add (regions);
855 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
857 vector<RegionView*> all_equivalent_regions;
859 get_regions_corresponding_to (region, all_equivalent_regions);
861 if (all_equivalent_regions.empty()) {
865 begin_reversible_command (_("set selected regions"));
868 case Selection::Toggle:
869 /* XXX this is not correct */
870 selection->toggle (all_equivalent_regions);
873 selection->set (all_equivalent_regions);
875 case Selection::Extend:
876 selection->add (all_equivalent_regions);
879 selection->add (all_equivalent_regions);
883 commit_reversible_command () ;
887 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
890 boost::shared_ptr<Region> r (weak_r.lock());
896 if ((rv = sv->find_view (r)) == 0) {
900 /* don't reset the selection if its something other than
901 a single other region.
904 if (selection->regions.size() > 1) {
908 begin_reversible_command (_("set selected regions"));
912 commit_reversible_command () ;
918 Editor::track_selection_changed ()
920 switch (selection->tracks.size()) {
924 set_selected_mixer_strip (*(selection->tracks.front()));
928 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
930 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
932 (*i)->set_selected (yn);
934 TimeAxisView::Children c = (*i)->get_child_list ();
935 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
936 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
940 ((mouse_mode == MouseRange) ||
941 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
942 (*i)->reshow_selection (selection->time);
944 (*i)->hide_selection ();
948 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
952 Editor::time_selection_changed ()
954 if (Profile->get_sae()) {
958 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
959 (*i)->hide_selection ();
962 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
963 (*i)->show_selection (selection->time);
966 if (selection->time.empty()) {
967 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
969 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
972 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
973 _session->request_locate (selection->time.start());
977 /** Set all region actions to have a given sensitivity */
979 Editor::sensitize_all_region_actions (bool s)
981 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
983 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
984 (*i)->set_sensitive (s);
987 _all_region_actions_sensitized = s;
990 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
991 * This method should be called just before displaying a Region menu. When a Region menu is not
992 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
993 * on entered_regionviews work without having to check sensitivity every time the selection or
994 * entered_regionview changes.
996 * This method also sets up toggle action state as appropriate.
999 Editor::sensitize_the_right_region_actions ()
1001 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1002 sensitize_all_region_actions (false);
1003 if (!selection->time.empty()) {
1004 _region_actions->get_action("split-region")->set_sensitive (true);
1008 } else if (mouse_mode != MouseObject) {
1009 sensitize_all_region_actions (false);
1013 /* We get here if we are in Object mode */
1015 RegionSelection rs = get_regions_from_selection_and_entered ();
1016 sensitize_all_region_actions (!rs.empty ());
1018 _ignore_region_action = true;
1020 /* Look through the regions that are selected and make notes about what we have got */
1022 bool have_audio = false;
1023 bool have_multichannel_audio = false;
1024 bool have_midi = false;
1025 bool have_locked = false;
1026 bool have_unlocked = false;
1027 bool have_position_lock_style_audio = false;
1028 bool have_position_lock_style_music = false;
1029 bool have_muted = false;
1030 bool have_unmuted = false;
1031 bool have_opaque = false;
1032 bool have_non_opaque = false;
1033 bool have_not_at_natural_position = false;
1034 bool have_envelope_visible = false;
1035 bool have_envelope_invisible = false;
1036 bool have_envelope_active = false;
1037 bool have_envelope_inactive = false;
1038 bool have_non_unity_scale_amplitude = false;
1039 bool have_compound_regions = false;
1041 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1043 boost::shared_ptr<Region> r = (*i)->region ();
1044 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1048 if (ar->n_channels() > 1) {
1049 have_multichannel_audio = true;
1053 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1057 if (r->is_compound()) {
1058 have_compound_regions = true;
1064 have_unlocked = true;
1067 if (r->position_lock_style() == MusicTime) {
1068 have_position_lock_style_music = true;
1070 have_position_lock_style_audio = true;
1076 have_unmuted = true;
1082 have_non_opaque = true;
1085 if (!r->at_natural_position()) {
1086 have_not_at_natural_position = true;
1090 /* its a bit unfortunate that "envelope visible" is a view-only
1091 property. we have to find the regionview to able to check
1092 its current setting.
1095 have_envelope_invisible = false;
1098 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1100 if (arv->envelope_visible()) {
1101 have_envelope_visible = true;
1103 have_envelope_invisible = true;
1108 if (ar->envelope_active()) {
1109 have_envelope_active = true;
1111 have_envelope_inactive = true;
1114 if (ar->scale_amplitude() != 1) {
1115 have_non_unity_scale_amplitude = true;
1120 if (rs.size() > 1) {
1121 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1122 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1123 _region_actions->get_action("rename-region")->set_sensitive (false);
1125 _region_actions->get_action("combine-regions")->set_sensitive (true);
1127 _region_actions->get_action("combine-regions")->set_sensitive (false);
1129 } else if (rs.size() == 1) {
1130 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1131 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1132 _region_actions->get_action("combine-regions")->set_sensitive (false);
1135 if (!have_multichannel_audio) {
1136 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1140 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1141 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1142 _region_actions->get_action("quantize-region")->set_sensitive (false);
1143 _region_actions->get_action("fork-region")->set_sensitive (false);
1144 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1145 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1146 _region_actions->get_action("transpose-region")->set_sensitive (false);
1148 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1149 /* others were already marked sensitive */
1152 if (_edit_point == EditAtMouse) {
1153 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1154 _region_actions->get_action("trim-front")->set_sensitive (false);
1155 _region_actions->get_action("trim-back")->set_sensitive (false);
1156 _region_actions->get_action("split-region")->set_sensitive (false);
1157 _region_actions->get_action("place-transient")->set_sensitive (false);
1160 if (have_compound_regions) {
1161 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1163 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1168 if (have_envelope_visible && !have_envelope_invisible) {
1169 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1170 } else if (have_envelope_visible && have_envelope_invisible) {
1171 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1174 if (have_envelope_active && !have_envelope_inactive) {
1175 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1176 } else if (have_envelope_active && have_envelope_inactive) {
1177 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1182 _region_actions->get_action("analyze-region")->set_sensitive (false);
1183 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1184 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1185 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1186 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1190 if (!have_non_unity_scale_amplitude || !have_audio) {
1191 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1194 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1195 if (have_locked && have_unlocked) {
1196 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1199 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);
1201 if (have_position_lock_style_music && have_position_lock_style_audio) {
1202 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1205 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1206 if (have_muted && have_unmuted) {
1207 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1210 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1211 if (have_opaque && have_non_opaque) {
1212 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1215 if (!have_not_at_natural_position) {
1216 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1219 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1220 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1221 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1223 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1226 _ignore_region_action = false;
1228 _all_region_actions_sensitized = false;
1233 Editor::region_selection_changed ()
1235 _regions->block_change_connection (true);
1236 editor_regions_selection_changed_connection.block(true);
1238 if (_region_selection_change_updates_region_list) {
1239 _regions->unselect_all ();
1242 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1243 (*i)->set_selected_regionviews (selection->regions);
1246 if (_region_selection_change_updates_region_list) {
1247 _regions->set_selected (selection->regions);
1250 _regions->block_change_connection (false);
1251 editor_regions_selection_changed_connection.block(false);
1253 if (!_all_region_actions_sensitized) {
1254 /* This selection change might have changed what region actions
1255 are allowed, so sensitize them all in case a key is pressed.
1257 sensitize_all_region_actions (true);
1260 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1261 _session->request_locate (selection->regions.start());
1266 Editor::point_selection_changed ()
1268 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1269 (*i)->set_selected_points (selection->points);
1274 Editor::select_all_in_track (Selection::Operation op)
1276 list<Selectable *> touched;
1278 if (!clicked_routeview) {
1282 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1285 case Selection::Toggle:
1286 selection->add (touched);
1288 case Selection::Set:
1289 selection->set (touched);
1291 case Selection::Extend:
1292 /* meaningless, because we're selecting everything */
1294 case Selection::Add:
1295 selection->add (touched);
1301 Editor::select_all_internal_edit (Selection::Operation)
1303 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1304 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1306 mrv->select_all_notes ();
1312 Editor::select_all (Selection::Operation op)
1314 list<Selectable *> touched;
1316 if (_internal_editing) {
1317 select_all_internal_edit (op);
1321 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1322 if ((*iter)->hidden()) {
1325 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1327 begin_reversible_command (_("select all"));
1329 case Selection::Add:
1330 selection->add (touched);
1332 case Selection::Toggle:
1333 selection->add (touched);
1335 case Selection::Set:
1336 selection->set (touched);
1338 case Selection::Extend:
1339 /* meaningless, because we're selecting everything */
1342 commit_reversible_command ();
1346 Editor::invert_selection_in_track ()
1348 list<Selectable *> touched;
1350 if (!clicked_routeview) {
1354 clicked_routeview->get_inverted_selectables (*selection, touched);
1355 selection->set (touched);
1359 Editor::invert_selection ()
1361 list<Selectable *> touched;
1363 if (_internal_editing) {
1364 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1365 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1367 mrv->invert_selection ();
1373 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1374 if ((*iter)->hidden()) {
1377 (*iter)->get_inverted_selectables (*selection, touched);
1380 selection->set (touched);
1383 /** @param start Start time in session frames.
1384 * @param end End time in session frames.
1385 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1386 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1387 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1388 * within the region are already selected.
1391 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1393 list<Selectable*> found;
1395 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1397 if ((*iter)->hidden()) {
1401 (*iter)->get_selectables (start, end, top, bot, found);
1404 if (found.empty()) {
1408 if (preserve_if_selected && op != Selection::Toggle) {
1409 list<Selectable*>::iterator i = found.begin();
1410 while (i != found.end() && (*i)->get_selected()) {
1414 if (i == found.end()) {
1419 begin_reversible_command (_("select all within"));
1421 case Selection::Add:
1422 selection->add (found);
1424 case Selection::Toggle:
1425 selection->toggle (found);
1427 case Selection::Set:
1428 selection->set (found);
1430 case Selection::Extend:
1431 /* not defined yet */
1435 commit_reversible_command ();
1439 Editor::set_selection_from_region ()
1441 if (selection->regions.empty()) {
1445 selection->set (selection->regions.start(), selection->regions.end_frame());
1446 if (!Profile->get_sae()) {
1447 set_mouse_mode (Editing::MouseRange, false);
1452 Editor::set_selection_from_punch()
1456 if ((location = _session->locations()->auto_punch_location()) == 0) {
1460 set_selection_from_range (*location);
1464 Editor::set_selection_from_loop()
1468 if ((location = _session->locations()->auto_loop_location()) == 0) {
1471 set_selection_from_range (*location);
1475 Editor::set_selection_from_range (Location& loc)
1477 begin_reversible_command (_("set selection from range"));
1478 selection->set (loc.start(), loc.end());
1479 commit_reversible_command ();
1481 if (!Profile->get_sae()) {
1482 set_mouse_mode (Editing::MouseRange, false);
1487 Editor::select_all_selectables_using_time_selection ()
1489 list<Selectable *> touched;
1491 if (selection->time.empty()) {
1495 framepos_t start = selection->time[clicked_selection].start;
1496 framepos_t end = selection->time[clicked_selection].end;
1498 if (end - start < 1) {
1504 if (selection->tracks.empty()) {
1507 ts = &selection->tracks;
1510 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1511 if ((*iter)->hidden()) {
1514 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1517 begin_reversible_command (_("select all from range"));
1518 selection->set (touched);
1519 commit_reversible_command ();
1524 Editor::select_all_selectables_using_punch()
1526 Location* location = _session->locations()->auto_punch_location();
1527 list<Selectable *> touched;
1529 if (location == 0 || (location->end() - location->start() <= 1)) {
1536 if (selection->tracks.empty()) {
1539 ts = &selection->tracks;
1542 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1543 if ((*iter)->hidden()) {
1546 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1548 begin_reversible_command (_("select all from punch"));
1549 selection->set (touched);
1550 commit_reversible_command ();
1555 Editor::select_all_selectables_using_loop()
1557 Location* location = _session->locations()->auto_loop_location();
1558 list<Selectable *> touched;
1560 if (location == 0 || (location->end() - location->start() <= 1)) {
1567 if (selection->tracks.empty()) {
1570 ts = &selection->tracks;
1573 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1574 if ((*iter)->hidden()) {
1577 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1579 begin_reversible_command (_("select all from loop"));
1580 selection->set (touched);
1581 commit_reversible_command ();
1586 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1590 list<Selectable *> touched;
1593 start = cursor->current_frame;
1594 end = _session->current_end_frame();
1596 if (cursor->current_frame > 0) {
1598 end = cursor->current_frame - 1;
1604 if (_internal_editing) {
1605 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1606 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1608 mrv->select_range (start, end);
1615 begin_reversible_command (_("select all after cursor"));
1617 begin_reversible_command (_("select all before cursor"));
1622 if (selection->tracks.empty()) {
1625 ts = &selection->tracks;
1628 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1629 if ((*iter)->hidden()) {
1632 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1634 selection->set (touched);
1635 commit_reversible_command ();
1639 Editor::select_all_selectables_using_edit (bool after)
1643 list<Selectable *> touched;
1646 start = get_preferred_edit_position();
1647 end = _session->current_end_frame();
1649 if ((end = get_preferred_edit_position()) > 1) {
1657 if (_internal_editing) {
1658 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1659 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1660 mrv->select_range (start, end);
1666 begin_reversible_command (_("select all after edit"));
1668 begin_reversible_command (_("select all before edit"));
1673 if (selection->tracks.empty()) {
1676 ts = &selection->tracks;
1679 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1680 if ((*iter)->hidden()) {
1683 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1685 selection->set (touched);
1686 commit_reversible_command ();
1690 Editor::select_all_selectables_between (bool /*within*/)
1694 list<Selectable *> touched;
1696 if (!get_edit_op_range (start, end)) {
1700 if (_internal_editing) {
1701 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1702 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1703 mrv->select_range (start, end);
1710 if (selection->tracks.empty()) {
1713 ts = &selection->tracks;
1716 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1717 if ((*iter)->hidden()) {
1720 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1723 selection->set (touched);
1727 Editor::select_range_between ()
1732 if (mouse_mode == MouseRange && !selection->time.empty()) {
1733 selection->clear_time ();
1736 if (!get_edit_op_range (start, end)) {
1740 set_mouse_mode (MouseRange);
1741 selection->set (start, end);
1745 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1750 /* in range mode, use any existing selection */
1752 if (mouse_mode == MouseRange && !selection->time.empty()) {
1753 /* we know that these are ordered */
1754 start = selection->time.start();
1755 end = selection->time.end_frame();
1759 if (!mouse_frame (m, ignored)) {
1760 /* mouse is not in a canvas, try playhead+selected marker.
1761 this is probably most true when using menus.
1764 if (selection->markers.empty()) {
1768 start = selection->markers.front()->position();
1769 end = _session->audible_frame();
1773 switch (_edit_point) {
1774 case EditAtPlayhead:
1775 if (selection->markers.empty()) {
1776 /* use mouse + playhead */
1778 end = _session->audible_frame();
1780 /* use playhead + selected marker */
1781 start = _session->audible_frame();
1782 end = selection->markers.front()->position();
1787 /* use mouse + selected marker */
1788 if (selection->markers.empty()) {
1790 end = _session->audible_frame();
1792 start = selection->markers.front()->position();
1797 case EditAtSelectedMarker:
1798 /* use mouse + selected marker */
1799 if (selection->markers.empty()) {
1801 MessageDialog win (_("No edit range defined"),
1806 win.set_secondary_text (
1807 _("the edit point is Selected Marker\nbut there is no selected marker."));
1810 win.set_default_response (RESPONSE_CLOSE);
1811 win.set_position (Gtk::WIN_POS_MOUSE);
1816 return false; // NO RANGE
1818 start = selection->markers.front()->position();
1832 /* turn range into one delimited by start...end,
1842 Editor::deselect_all ()
1844 selection->clear ();
1848 Editor::select_range (framepos_t s, framepos_t e)
1850 selection->add (clicked_axisview);
1851 selection->time.clear ();
1852 return selection->set (s, e);