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/midi_region.h"
26 #include "ardour/playlist.h"
27 #include "ardour/profile.h"
28 #include "ardour/route_group.h"
29 #include "ardour/session.h"
31 #include "control_protocol/control_protocol.h"
33 #include "editor_drag.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"
49 using namespace ARDOUR;
53 using namespace Gtkmm2ext;
54 using namespace Editing;
56 struct TrackViewByPositionSorter
58 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
59 return a->y_position() < b->y_position();
64 Editor::extend_selection_to_track (TimeAxisView& view)
66 if (selection->selected (&view)) {
67 /* already selected, do nothing */
71 if (selection->tracks.empty()) {
73 if (!selection->selected (&view)) {
74 selection->set (&view);
81 /* something is already selected, so figure out which range of things to add */
83 TrackViewList to_be_added;
84 TrackViewList sorted = track_views;
85 TrackViewByPositionSorter cmp;
86 bool passed_clicked = false;
91 if (!selection->selected (&view)) {
92 to_be_added.push_back (&view);
95 /* figure out if we should go forward or backwards */
97 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
100 passed_clicked = true;
103 if (selection->selected (*i)) {
104 if (passed_clicked) {
113 passed_clicked = false;
117 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
120 passed_clicked = true;
124 if (passed_clicked) {
125 if ((*i)->hidden()) {
128 if (selection->selected (*i)) {
130 } else if (!(*i)->hidden()) {
131 to_be_added.push_back (*i);
138 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
141 passed_clicked = true;
145 if (passed_clicked) {
147 if ((*r)->hidden()) {
151 if (selection->selected (*r)) {
153 } else if (!(*r)->hidden()) {
154 to_be_added.push_back (*r);
160 if (!to_be_added.empty()) {
161 selection->add (to_be_added);
169 Editor::select_all_tracks ()
171 TrackViewList visible_views;
172 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
173 if ((*i)->marked_for_display()) {
174 visible_views.push_back (*i);
177 selection->set (visible_views);
180 /** Select clicked_axisview, unless there are no currently selected
181 * tracks, in which case nothing will happen unless `force' is true.
184 Editor::set_selected_track_as_side_effect (Selection::Operation op)
186 if (!clicked_axisview) {
190 RouteGroup* group = NULL;
191 if (clicked_routeview) {
192 group = clicked_routeview->route()->route_group();
195 bool had_tracks = !selection->tracks.empty();
196 RouteGroup& arg (_session->all_route_group());
199 case Selection::Toggle:
200 if (selection->selected (clicked_axisview)) {
201 if (arg.is_select() && arg.is_active()) {
202 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
203 selection->remove(*i);
205 } else if (group && group->is_active()) {
206 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
207 if ((*i)->route_group() == group) {
208 selection->remove(*i);
212 selection->remove (clicked_axisview);
215 if (arg.is_select() && arg.is_active()) {
216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 } else if (group && group->is_active()) {
220 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
221 if ((*i)->route_group() == group) {
226 selection->add (clicked_axisview);
232 if (!had_tracks && arg.is_select() && arg.is_active()) {
233 /* nothing was selected already, and all group is active etc. so use
236 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 } else if (group && group->is_active()) {
240 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
241 if ((*i)->route_group() == group) {
246 selection->add (clicked_axisview);
252 if (!had_tracks && arg.is_select() && arg.is_active()) {
253 /* nothing was selected already, and all group is active etc. so use
256 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
259 } else if (group && group->is_active()) {
260 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
261 if ((*i)->route_group() == group) {
266 selection->set (clicked_axisview);
270 case Selection::Extend:
277 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
279 begin_reversible_selection_op (X_("Set Selected Track"));
282 case Selection::Toggle:
283 if (selection->selected (&view)) {
285 selection->remove (&view);
288 selection->add (&view);
293 if (!selection->selected (&view)) {
294 selection->add (&view);
299 selection->set (&view);
302 case Selection::Extend:
303 extend_selection_to_track (view);
307 commit_reversible_selection_op ();
311 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
313 if (!clicked_routeview) {
321 set_selected_track (*clicked_routeview, op, no_remove);
325 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
327 if (!clicked_control_point) {
335 selection->set (clicked_control_point);
341 selection->add (clicked_control_point);
345 case Selection::Toggle:
347 /* This is a bit of a hack; if we Primary-Click-Drag a control
348 point (for push drag) we want the point we clicked on to be
349 selected, otherwise we end up confusingly dragging an
350 unselected point. So here we ensure that the point is selected
351 after the press, and if we subsequently get a release (meaning no
352 drag occurred) we set things up so that the toggle has happened.
354 if (press && !selection->selected (clicked_control_point)) {
355 /* This is the button press, and the control point is not selected; make it so,
356 in case this press leads to a drag. Also note that having done this, we don't
357 need to toggle again on release.
359 selection->toggle (clicked_control_point);
360 _control_point_toggled_on_press = true;
362 } else if (!press && !_control_point_toggled_on_press) {
363 /* This is the release, and the point wasn't toggled on the press, so do it now */
364 selection->toggle (clicked_control_point);
368 _control_point_toggled_on_press = false;
371 case Selection::Extend:
380 Editor::get_onscreen_tracks (TrackViewList& tvl)
382 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
383 if ((*i)->y_position() < _visible_canvas_height) {
389 /** Call a slot for a given `basis' track and also for any track that is in the same
390 * active route group with a particular set of properties.
392 * @param sl Slot to call.
393 * @param basis Basis track.
394 * @param prop Properties that active edit groups must share to be included in the map.
398 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
400 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
402 if (route_basis == 0) {
406 set<RouteTimeAxisView*> tracks;
407 tracks.insert (route_basis);
409 RouteGroup* group = route_basis->route()->route_group();
411 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
413 /* the basis is a member of an active route group, with the appropriate
414 properties; find other members */
416 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
417 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
418 if (v && v->route()->route_group() == group) {
425 uint32_t const sz = tracks.size ();
427 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
432 /** Call a slot for a given `basis' track and also for any track that is in the same
433 * active route group with a particular set of properties.
435 * @param sl Slot to call.
436 * @param basis Basis track.
437 * @param prop Properties that active edit groups must share to be included in the map.
441 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
443 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
444 set<boost::shared_ptr<Playlist> > playlists;
446 if (route_basis == 0) {
450 set<RouteTimeAxisView*> tracks;
451 tracks.insert (route_basis);
453 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
455 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
457 /* the basis is a member of an active route group, with the appropriate
458 properties; find other members */
460 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
461 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
463 if (v && v->route()->route_group() == group) {
465 boost::shared_ptr<Track> t = v->track();
467 if (playlists.insert (t->playlist()).second) {
468 /* haven't seen this playlist yet */
472 /* not actually a "Track", but a timeaxis view that
473 we should mapover anyway.
482 uint32_t const sz = tracks.size ();
484 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
490 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
492 boost::shared_ptr<Playlist> pl;
493 vector<boost::shared_ptr<Region> > results;
495 boost::shared_ptr<Track> tr;
497 if ((tr = tv.track()) == 0) {
502 if (&tv == &basis->get_time_axis_view()) {
503 /* looking in same track as the original */
507 if ((pl = tr->playlist()) != 0) {
508 pl->get_equivalent_regions (basis->region(), results);
511 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
512 if ((marv = tv.view()->find_view (*ir)) != 0) {
513 all_equivs->push_back (marv);
519 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
521 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);
523 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
525 equivalent_regions.push_back (basis);
529 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
531 RegionSelection equivalent;
533 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
535 vector<RegionView*> eq;
537 mapover_tracks_with_unique_playlists (
538 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
539 &(*i)->get_time_axis_view(), prop);
541 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
552 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
554 int region_count = 0;
556 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
558 RouteTimeAxisView* tatv;
560 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
562 boost::shared_ptr<Playlist> pl;
563 vector<boost::shared_ptr<Region> > results;
565 boost::shared_ptr<Track> tr;
567 if ((tr = tatv->track()) == 0) {
572 if ((pl = (tr->playlist())) != 0) {
573 pl->get_region_list_equivalent_regions (region, results);
576 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
577 if ((marv = tatv->view()->find_view (*ir)) != 0) {
590 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
592 vector<RegionView*> all_equivalent_regions;
595 if (!clicked_regionview || !clicked_routeview) {
600 button_release_can_deselect = false;
603 if (op == Selection::Toggle || op == Selection::Set) {
606 case Selection::Toggle:
607 if (selection->selected (clicked_regionview)) {
610 /* whatever was clicked was selected already; do nothing here but allow
611 the button release to deselect it
614 button_release_can_deselect = true;
617 if (button_release_can_deselect) {
619 /* just remove this one region, but only on a permitted button release */
621 selection->remove (clicked_regionview);
624 /* no more deselect action on button release till a new press
625 finds an already selected object.
628 button_release_can_deselect = false;
636 if (selection->selected (clicked_routeview)) {
637 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
639 all_equivalent_regions.push_back (clicked_regionview);
642 /* add all the equivalent regions, but only on button press */
644 if (!all_equivalent_regions.empty()) {
648 selection->add (all_equivalent_regions);
654 if (!selection->selected (clicked_regionview)) {
655 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
656 selection->set (all_equivalent_regions);
659 /* clicked on an already selected region */
663 if (selection->regions.size() > 1) {
664 /* collapse region selection down to just this one region (and its equivalents) */
665 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
666 selection->set(all_equivalent_regions);
678 } else if (op == Selection::Extend) {
680 list<Selectable*> results;
681 framepos_t last_frame;
682 framepos_t first_frame;
683 bool same_track = false;
685 /* 1. find the last selected regionview in the track that was clicked in */
688 first_frame = max_framepos;
690 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
691 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
693 if ((*x)->region()->last_frame() > last_frame) {
694 last_frame = (*x)->region()->last_frame();
697 if ((*x)->region()->first_frame() < first_frame) {
698 first_frame = (*x)->region()->first_frame();
707 /* 2. figure out the boundaries for our search for new objects */
709 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
710 case Evoral::OverlapNone:
711 if (last_frame < clicked_regionview->region()->first_frame()) {
712 first_frame = last_frame;
713 last_frame = clicked_regionview->region()->last_frame();
715 last_frame = first_frame;
716 first_frame = clicked_regionview->region()->first_frame();
720 case Evoral::OverlapExternal:
721 if (last_frame < clicked_regionview->region()->first_frame()) {
722 first_frame = last_frame;
723 last_frame = clicked_regionview->region()->last_frame();
725 last_frame = first_frame;
726 first_frame = clicked_regionview->region()->first_frame();
730 case Evoral::OverlapInternal:
731 if (last_frame < clicked_regionview->region()->first_frame()) {
732 first_frame = last_frame;
733 last_frame = clicked_regionview->region()->last_frame();
735 last_frame = first_frame;
736 first_frame = clicked_regionview->region()->first_frame();
740 case Evoral::OverlapStart:
741 case Evoral::OverlapEnd:
742 /* nothing to do except add clicked region to selection, since it
743 overlaps with the existing selection in this track.
750 /* click in a track that has no regions selected, so extend vertically
751 to pick out all regions that are defined by the existing selection
756 first_frame = clicked_regionview->region()->position();
757 last_frame = clicked_regionview->region()->last_frame();
759 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
760 if ((*i)->region()->position() < first_frame) {
761 first_frame = (*i)->region()->position();
763 if ((*i)->region()->last_frame() + 1 > last_frame) {
764 last_frame = (*i)->region()->last_frame();
769 /* 2. find all the tracks we should select in */
771 set<RouteTimeAxisView*> relevant_tracks;
773 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
774 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
776 relevant_tracks.insert (r);
780 set<RouteTimeAxisView*> already_in_selection;
782 if (relevant_tracks.empty()) {
784 /* no tracks selected .. thus .. if the
785 regionview we're in isn't selected
786 (i.e. we're about to extend to it), then
787 find all tracks between the this one and
791 if (!selection->selected (clicked_regionview)) {
793 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
797 /* add this track to the ones we will search */
799 relevant_tracks.insert (rtv);
801 /* find the track closest to this one that
802 already a selected region.
805 RouteTimeAxisView* closest = 0;
806 int distance = INT_MAX;
807 int key = rtv->route()->order_key ();
809 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
811 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
813 if (artv && artv != rtv) {
815 pair<set<RouteTimeAxisView*>::iterator,bool> result;
817 result = already_in_selection.insert (artv);
820 /* newly added to already_in_selection */
822 int d = artv->route()->order_key ();
826 if (abs (d) < distance) {
836 /* now add all tracks between that one and this one */
838 int okey = closest->route()->order_key ();
844 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
845 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
846 if (artv && artv != rtv) {
848 int k = artv->route()->order_key ();
850 if (k >= okey && k <= key) {
852 /* in range but don't add it if
853 it already has tracks selected.
854 this avoids odd selection
855 behaviour that feels wrong.
858 if (find (already_in_selection.begin(),
859 already_in_selection.end(),
860 artv) == already_in_selection.end()) {
862 relevant_tracks.insert (artv);
872 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
873 one that was clicked.
876 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
877 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
880 /* 4. convert to a vector of regions */
882 vector<RegionView*> regions;
884 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
887 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
888 regions.push_back (arv);
892 if (!regions.empty()) {
893 selection->add (regions);
904 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
906 vector<RegionView*> all_equivalent_regions;
908 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
910 if (all_equivalent_regions.empty()) {
914 begin_reversible_selection_op (X_("set selected regions"));
917 case Selection::Toggle:
918 /* XXX this is not correct */
919 selection->toggle (all_equivalent_regions);
922 selection->set (all_equivalent_regions);
924 case Selection::Extend:
925 selection->add (all_equivalent_regions);
928 selection->add (all_equivalent_regions);
932 commit_reversible_selection_op () ;
936 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
939 boost::shared_ptr<Region> r (weak_r.lock());
945 if ((rv = sv->find_view (r)) == 0) {
949 /* don't reset the selection if its something other than
950 a single other region.
953 if (selection->regions.size() > 1) {
957 begin_reversible_selection_op (X_("set selected regions"));
961 commit_reversible_selection_op () ;
967 Editor::track_selection_changed ()
969 switch (selection->tracks.size()) {
973 set_selected_mixer_strip (*(selection->tracks.front()));
977 RouteNotificationListPtr routes (new RouteNotificationList);
979 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
981 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
983 (*i)->set_selected (yn);
985 TimeAxisView::Children c = (*i)->get_child_list ();
986 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
987 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
991 (*i)->reshow_selection (selection->time);
993 (*i)->hide_selection ();
998 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
1000 routes->push_back (rtav->route());
1005 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1007 /* notify control protocols */
1009 ControlProtocol::TrackSelectionChanged (routes);
1011 if (sfbrowser && _session && !_session->deletion_in_progress()) {
1012 uint32_t audio_track_cnt = 0;
1013 uint32_t midi_track_cnt = 0;
1015 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
1016 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x);
1019 if (atv->is_audio_track()) {
1024 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
1027 if (mtv->is_midi_track()) {
1034 sfbrowser->reset (audio_track_cnt, midi_track_cnt);
1039 Editor::time_selection_changed ()
1041 if (Profile->get_sae()) {
1045 /* XXX this is superficially inefficient. Hide the selection in all
1046 * tracks, then show it in all selected tracks.
1048 * However, if you investigate what this actually does, it isn't
1049 * anywhere nearly as bad as it may appear. Remember: nothing is
1050 * redrawn or even recomputed during these two loops - that only
1051 * happens when we next render ...
1054 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1055 (*i)->hide_selection ();
1058 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1059 (*i)->show_selection (selection->time);
1062 if (selection->time.empty()) {
1063 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1065 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1068 /* propagate into backend, but only when there is no drag or we are at
1069 * the end of a drag, otherwise this is too expensive (could case a
1070 * locate per mouse motion event.
1073 if (_session && !_drags->active()) {
1074 if (selection->time.length() != 0) {
1075 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1077 _session->clear_range_selection ();
1082 /** Set all region actions to have a given sensitivity */
1084 Editor::sensitize_all_region_actions (bool s)
1086 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1088 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1089 (*i)->set_sensitive (s);
1092 _all_region_actions_sensitized = s;
1095 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1096 * This method should be called just before displaying a Region menu. When a Region menu is not
1097 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1098 * on entered_regionviews work without having to check sensitivity every time the selection or
1099 * entered_regionview changes.
1101 * This method also sets up toggle action state as appropriate.
1104 Editor::sensitize_the_right_region_actions ()
1107 RegionSelection rs = get_regions_from_selection_and_entered ();
1108 sensitize_all_region_actions (!rs.empty ());
1110 _ignore_region_action = true;
1112 /* Look through the regions that are selected and make notes about what we have got */
1114 bool have_audio = false;
1115 bool have_multichannel_audio = false;
1116 bool have_midi = false;
1117 bool have_locked = false;
1118 bool have_unlocked = false;
1119 bool have_video_locked = false;
1120 bool have_video_unlocked = false;
1121 bool have_position_lock_style_audio = false;
1122 bool have_position_lock_style_music = false;
1123 bool have_muted = false;
1124 bool have_unmuted = false;
1125 bool have_opaque = false;
1126 bool have_non_opaque = false;
1127 bool have_not_at_natural_position = false;
1128 bool have_envelope_active = false;
1129 bool have_envelope_inactive = false;
1130 bool have_non_unity_scale_amplitude = false;
1131 bool have_compound_regions = false;
1132 bool have_inactive_fade_in = false;
1133 bool have_inactive_fade_out = false;
1134 bool have_active_fade_in = false;
1135 bool have_active_fade_out = false;
1137 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1139 boost::shared_ptr<Region> r = (*i)->region ();
1140 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1144 if (ar->n_channels() > 1) {
1145 have_multichannel_audio = true;
1149 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1153 if (r->is_compound()) {
1154 have_compound_regions = true;
1160 have_unlocked = true;
1163 if (r->video_locked()) {
1164 have_video_locked = true;
1166 have_video_unlocked = true;
1169 if (r->position_lock_style() == MusicTime) {
1170 have_position_lock_style_music = true;
1172 have_position_lock_style_audio = true;
1178 have_unmuted = true;
1184 have_non_opaque = true;
1187 if (!r->at_natural_position()) {
1188 have_not_at_natural_position = true;
1192 if (ar->envelope_active()) {
1193 have_envelope_active = true;
1195 have_envelope_inactive = true;
1198 if (ar->scale_amplitude() != 1) {
1199 have_non_unity_scale_amplitude = true;
1202 if (ar->fade_in_active ()) {
1203 have_active_fade_in = true;
1205 have_inactive_fade_in = true;
1208 if (ar->fade_out_active ()) {
1209 have_active_fade_out = true;
1211 have_inactive_fade_out = true;
1216 if (rs.size() > 1) {
1217 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1218 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1219 _region_actions->get_action("rename-region")->set_sensitive (false);
1221 /* XXX need to check whether there is than 1 per
1222 playlist, because otherwise this makes no sense.
1224 _region_actions->get_action("combine-regions")->set_sensitive (true);
1226 _region_actions->get_action("combine-regions")->set_sensitive (false);
1228 } else if (rs.size() == 1) {
1229 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1230 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1231 _region_actions->get_action("combine-regions")->set_sensitive (false);
1234 if (!have_multichannel_audio) {
1235 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1239 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1240 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1241 _region_actions->get_action("quantize-region")->set_sensitive (false);
1242 _region_actions->get_action("legatize-region")->set_sensitive (false);
1243 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1244 _region_actions->get_action("fork-region")->set_sensitive (false);
1245 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1246 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1247 _region_actions->get_action("transpose-region")->set_sensitive (false);
1249 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1250 /* others were already marked sensitive */
1253 if (_edit_point == EditAtMouse) {
1254 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1255 _region_actions->get_action("trim-front")->set_sensitive (false);
1256 _region_actions->get_action("trim-back")->set_sensitive (false);
1257 _region_actions->get_action("split-region")->set_sensitive (false);
1258 _region_actions->get_action("place-transient")->set_sensitive (false);
1261 if (have_compound_regions) {
1262 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1264 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1269 if (have_envelope_active && !have_envelope_inactive) {
1270 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1271 } else if (have_envelope_active && have_envelope_inactive) {
1272 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1277 _region_actions->get_action("analyze-region")->set_sensitive (false);
1278 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1279 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1280 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1284 if (!have_non_unity_scale_amplitude || !have_audio) {
1285 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1288 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1289 a->set_active (have_locked && !have_unlocked);
1290 if (have_locked && have_unlocked) {
1291 // a->set_inconsistent ();
1294 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1295 a->set_active (have_video_locked && !have_video_unlocked);
1296 if (have_video_locked && have_video_unlocked) {
1297 // a->set_inconsistent ();
1300 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1301 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1303 if (have_position_lock_style_music && have_position_lock_style_audio) {
1304 // a->set_inconsistent ();
1307 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1308 a->set_active (have_muted && !have_unmuted);
1309 if (have_muted && have_unmuted) {
1310 // a->set_inconsistent ();
1313 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1314 a->set_active (have_opaque && !have_non_opaque);
1315 if (have_opaque && have_non_opaque) {
1316 // a->set_inconsistent ();
1319 if (!have_not_at_natural_position) {
1320 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1323 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1324 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1325 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1327 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1330 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1331 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1332 if (have_active_fade_in && have_inactive_fade_in) {
1333 // a->set_inconsistent ();
1336 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1337 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1339 if (have_active_fade_out && have_inactive_fade_out) {
1340 // a->set_inconsistent ();
1343 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1344 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1346 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1347 a->set_active (have_active_fade && !have_inactive_fade);
1349 if (have_active_fade && have_inactive_fade) {
1350 // a->set_inconsistent ();
1353 _ignore_region_action = false;
1355 _all_region_actions_sensitized = false;
1360 Editor::region_selection_changed ()
1362 _regions->block_change_connection (true);
1363 editor_regions_selection_changed_connection.block(true);
1365 if (_region_selection_change_updates_region_list) {
1366 _regions->unselect_all ();
1369 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1370 (*i)->set_selected_regionviews (selection->regions);
1373 if (_region_selection_change_updates_region_list) {
1374 _regions->set_selected (selection->regions);
1377 _regions->block_change_connection (false);
1378 editor_regions_selection_changed_connection.block(false);
1380 if (!_all_region_actions_sensitized) {
1381 /* This selection change might have changed what region actions
1382 are allowed, so sensitize them all in case a key is pressed.
1384 sensitize_all_region_actions (true);
1387 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1388 maybe_locate_with_edit_preroll (selection->regions.start());
1391 /* propagate into backend */
1394 if (!selection->regions.empty()) {
1395 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1397 _session->clear_object_selection ();
1404 Editor::point_selection_changed ()
1406 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1407 (*i)->set_selected_points (selection->points);
1412 Editor::select_all_in_track (Selection::Operation op)
1414 list<Selectable *> touched;
1416 if (!clicked_routeview) {
1420 begin_reversible_selection_op (X_("Select All in Track"));
1422 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1425 case Selection::Toggle:
1426 selection->add (touched);
1428 case Selection::Set:
1429 selection->set (touched);
1431 case Selection::Extend:
1432 /* meaningless, because we're selecting everything */
1434 case Selection::Add:
1435 selection->add (touched);
1439 commit_reversible_selection_op ();
1443 Editor::select_all_internal_edit (Selection::Operation)
1445 bool selected = false;
1447 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1448 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1450 mrv->select_all_notes ();
1455 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1457 mrv->select_all_notes ();
1465 Editor::select_all_objects (Selection::Operation op)
1467 list<Selectable *> touched;
1469 TrackViewList ts = track_views;
1471 if (internal_editing() && select_all_internal_edit(op)) {
1472 return; // Selected notes
1475 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1476 if ((*iter)->hidden()) {
1479 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1480 selection->add (*iter);
1484 begin_reversible_selection_op (X_("select all"));
1486 case Selection::Add:
1487 selection->add (touched);
1489 case Selection::Toggle:
1490 selection->add (touched);
1492 case Selection::Set:
1493 selection->set (touched);
1495 case Selection::Extend:
1496 /* meaningless, because we're selecting everything */
1499 commit_reversible_selection_op ();
1503 Editor::invert_selection_in_track ()
1505 list<Selectable *> touched;
1507 if (!clicked_routeview) {
1511 begin_reversible_selection_op (X_("Invert Selection in Track"));
1512 clicked_routeview->get_inverted_selectables (*selection, touched);
1513 selection->set (touched);
1514 commit_reversible_selection_op ();
1518 Editor::invert_selection ()
1520 list<Selectable *> touched;
1522 if (internal_editing()) {
1523 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1524 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1526 mrv->invert_selection ();
1532 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1533 if ((*iter)->hidden()) {
1536 (*iter)->get_inverted_selectables (*selection, touched);
1539 begin_reversible_selection_op (X_("Invert Selection"));
1540 selection->set (touched);
1541 commit_reversible_selection_op ();
1544 /** @param start Start time in session frames.
1545 * @param end End time in session frames.
1546 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1547 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1548 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1549 * within the region are already selected.
1552 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1554 list<Selectable*> found;
1556 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1558 if ((*iter)->hidden()) {
1562 (*iter)->get_selectables (start, end, top, bot, found);
1565 if (found.empty()) {
1566 selection->clear_objects();
1567 selection->clear_time ();
1571 if (preserve_if_selected && op != Selection::Toggle) {
1572 list<Selectable*>::iterator i = found.begin();
1573 while (i != found.end() && (*i)->get_selected()) {
1577 if (i == found.end()) {
1582 begin_reversible_selection_op (X_("select all within"));
1584 case Selection::Add:
1585 selection->add (found);
1587 case Selection::Toggle:
1588 selection->toggle (found);
1590 case Selection::Set:
1591 selection->set (found);
1593 case Selection::Extend:
1594 /* not defined yet */
1598 commit_reversible_selection_op ();
1602 Editor::set_selection_from_region ()
1604 if (selection->regions.empty()) {
1608 /* find all the tracks that have selected regions */
1610 set<TimeAxisView*> tracks;
1612 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1613 tracks.insert (&(*r)->get_time_axis_view());
1617 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1619 /* select range (this will clear the region selection) */
1621 selection->set (selection->regions.start(), selection->regions.end_frame());
1623 /* and select the tracks */
1625 selection->set (tvl);
1627 if (!Profile->get_sae()) {
1628 set_mouse_mode (Editing::MouseRange, false);
1633 Editor::set_selection_from_punch()
1637 if ((location = _session->locations()->auto_punch_location()) == 0) {
1641 set_selection_from_range (*location);
1645 Editor::set_selection_from_loop()
1649 if ((location = _session->locations()->auto_loop_location()) == 0) {
1652 set_selection_from_range (*location);
1656 Editor::set_selection_from_range (Location& loc)
1658 begin_reversible_selection_op (X_("set selection from range"));
1659 selection->set (loc.start(), loc.end());
1660 commit_reversible_selection_op ();
1662 if (!Profile->get_sae()) {
1663 set_mouse_mode (Editing::MouseRange, false);
1668 Editor::select_all_selectables_using_time_selection ()
1670 list<Selectable *> touched;
1672 if (selection->time.empty()) {
1676 framepos_t start = selection->time[clicked_selection].start;
1677 framepos_t end = selection->time[clicked_selection].end;
1679 if (end - start < 1) {
1685 if (selection->tracks.empty()) {
1688 ts = &selection->tracks;
1691 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1692 if ((*iter)->hidden()) {
1695 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1698 begin_reversible_selection_op (X_("select all from range"));
1699 selection->set (touched);
1700 commit_reversible_selection_op ();
1705 Editor::select_all_selectables_using_punch()
1707 Location* location = _session->locations()->auto_punch_location();
1708 list<Selectable *> touched;
1710 if (location == 0 || (location->end() - location->start() <= 1)) {
1717 if (selection->tracks.empty()) {
1720 ts = &selection->tracks;
1723 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1724 if ((*iter)->hidden()) {
1727 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1729 begin_reversible_selection_op (X_("select all from punch"));
1730 selection->set (touched);
1731 commit_reversible_selection_op ();
1736 Editor::select_all_selectables_using_loop()
1738 Location* location = _session->locations()->auto_loop_location();
1739 list<Selectable *> touched;
1741 if (location == 0 || (location->end() - location->start() <= 1)) {
1748 if (selection->tracks.empty()) {
1751 ts = &selection->tracks;
1754 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1755 if ((*iter)->hidden()) {
1758 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1760 begin_reversible_selection_op (X_("select all from loop"));
1761 selection->set (touched);
1762 commit_reversible_selection_op ();
1767 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1771 list<Selectable *> touched;
1774 start = cursor->current_frame();
1775 end = _session->current_end_frame();
1777 if (cursor->current_frame() > 0) {
1779 end = cursor->current_frame() - 1;
1785 if (internal_editing()) {
1786 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1787 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1789 mrv->select_range (start, end);
1796 begin_reversible_selection_op (X_("select all after cursor"));
1798 begin_reversible_selection_op (X_("select all before cursor"));
1803 if (selection->tracks.empty()) {
1806 ts = &selection->tracks;
1809 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1810 if ((*iter)->hidden()) {
1813 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1815 selection->set (touched);
1816 commit_reversible_selection_op ();
1820 Editor::select_all_selectables_using_edit (bool after)
1824 list<Selectable *> touched;
1827 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1828 end = _session->current_end_frame();
1830 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1838 if (internal_editing()) {
1839 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1840 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1841 mrv->select_range (start, end);
1847 begin_reversible_selection_op (X_("select all after edit"));
1849 begin_reversible_selection_op (X_("select all before edit"));
1854 if (selection->tracks.empty()) {
1857 ts = &selection->tracks;
1860 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1861 if ((*iter)->hidden()) {
1864 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1866 selection->set (touched);
1867 commit_reversible_selection_op ();
1871 Editor::select_all_selectables_between (bool within)
1875 list<Selectable *> touched;
1877 if (!get_edit_op_range (start, end)) {
1881 if (internal_editing()) {
1882 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1883 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1884 mrv->select_range (start, end);
1891 if (selection->tracks.empty()) {
1894 ts = &selection->tracks;
1897 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1898 if ((*iter)->hidden()) {
1901 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1904 begin_reversible_selection_op (X_("Select all Selectables Between"));
1905 selection->set (touched);
1906 commit_reversible_selection_op ();
1910 Editor::select_range_between ()
1915 if ( !selection->time.empty() ) {
1916 selection->clear_time ();
1919 if (!get_edit_op_range (start, end)) {
1923 begin_reversible_selection_op (X_("Select Range Between"));
1924 set_mouse_mode (MouseRange);
1925 selection->set (start, end);
1926 commit_reversible_selection_op ();
1930 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1935 /* if an explicit range exists, use it */
1937 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1938 /* we know that these are ordered */
1939 start = selection->time.start();
1940 end = selection->time.end_frame();
1948 // if (!mouse_frame (m, ignored)) {
1949 // /* mouse is not in a canvas, try playhead+selected marker.
1950 // this is probably most true when using menus.
1953 // if (selection->markers.empty()) {
1957 // start = selection->markers.front()->position();
1958 // end = _session->audible_frame();
1962 // switch (_edit_point) {
1963 // case EditAtPlayhead:
1964 // if (selection->markers.empty()) {
1965 // /* use mouse + playhead */
1967 // end = _session->audible_frame();
1969 // /* use playhead + selected marker */
1970 // start = _session->audible_frame();
1971 // end = selection->markers.front()->position();
1975 // case EditAtMouse:
1976 // /* use mouse + selected marker */
1977 // if (selection->markers.empty()) {
1979 // end = _session->audible_frame();
1981 // start = selection->markers.front()->position();
1986 // case EditAtSelectedMarker:
1987 // /* use mouse + selected marker */
1988 // if (selection->markers.empty()) {
1990 // MessageDialog win (_("No edit range defined"),
1995 // win.set_secondary_text (
1996 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1999 // win.set_default_response (RESPONSE_CLOSE);
2000 // win.set_position (Gtk::WIN_POS_MOUSE);
2005 // return false; // NO RANGE
2007 // start = selection->markers.front()->position();
2013 // if (start == end) {
2017 // if (start > end) {
2018 // swap (start, end);
2021 /* turn range into one delimited by start...end,
2031 Editor::deselect_all ()
2033 begin_reversible_selection_op (X_("Deselect All"));
2034 selection->clear ();
2035 commit_reversible_selection_op ();
2039 Editor::select_range (framepos_t s, framepos_t e)
2041 begin_reversible_selection_op (X_("Select Range"));
2042 selection->add (clicked_axisview);
2043 selection->time.clear ();
2044 long ret = selection->set (s, e);
2045 commit_reversible_selection_op ();