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"
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 RouteGroup* group = NULL;
190 if (clicked_routeview) {
191 group = clicked_routeview->route()->route_group();
194 bool had_tracks = !selection->tracks.empty();
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);
211 selection->remove (clicked_axisview);
214 if (arg.is_select() && arg.is_active()) {
215 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
218 } else if (group && group->is_active()) {
219 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
220 if ((*i)->route_group() == group) {
225 selection->add (clicked_axisview);
231 if (!had_tracks && arg.is_select() && arg.is_active()) {
232 /* nothing was selected already, and all group is active etc. so use
235 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
238 } else if (group && group->is_active()) {
239 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
240 if ((*i)->route_group() == group) {
245 selection->add (clicked_axisview);
251 if (!had_tracks && arg.is_select() && arg.is_active()) {
252 /* nothing was selected already, and all group is active etc. so use
255 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
258 } else if (group && group->is_active()) {
259 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
260 if ((*i)->route_group() == group) {
265 selection->set (clicked_axisview);
269 case Selection::Extend:
276 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
278 begin_reversible_selection_op (X_("Set Selected Track"));
281 case Selection::Toggle:
282 if (selection->selected (&view)) {
284 selection->remove (&view);
287 selection->add (&view);
292 if (!selection->selected (&view)) {
293 selection->add (&view);
298 selection->set (&view);
301 case Selection::Extend:
302 extend_selection_to_track (view);
306 commit_reversible_selection_op ();
310 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
312 if (!clicked_routeview) {
320 set_selected_track (*clicked_routeview, op, no_remove);
324 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
326 if (!clicked_control_point) {
333 selection->set (clicked_control_point);
338 selection->add (clicked_control_point);
341 case Selection::Toggle:
342 /* This is a bit of a hack; if we Primary-Click-Drag a control
343 point (for push drag) we want the point we clicked on to be
344 selected, otherwise we end up confusingly dragging an
345 unselected point. So here we ensure that the point is selected
346 after the press, and if we subsequently get a release (meaning no
347 drag occurred) we set things up so that the toggle has happened.
349 if (press && !selection->selected (clicked_control_point)) {
350 /* This is the button press, and the control point is not selected; make it so,
351 in case this press leads to a drag. Also note that having done this, we don't
352 need to toggle again on release.
354 selection->toggle (clicked_control_point);
355 _control_point_toggled_on_press = true;
356 } else if (!press && !_control_point_toggled_on_press) {
357 /* This is the release, and the point wasn't toggled on the press, so do it now */
358 selection->toggle (clicked_control_point);
361 _control_point_toggled_on_press = false;
364 case Selection::Extend:
373 Editor::get_onscreen_tracks (TrackViewList& tvl)
375 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
376 if ((*i)->y_position() < _visible_canvas_height) {
382 /** Call a slot for a given `basis' track and also for any track that is in the same
383 * active route group with a particular set of properties.
385 * @param sl Slot to call.
386 * @param basis Basis track.
387 * @param prop Properties that active edit groups must share to be included in the map.
391 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
393 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
395 if (route_basis == 0) {
399 set<RouteTimeAxisView*> tracks;
400 tracks.insert (route_basis);
402 RouteGroup* group = route_basis->route()->route_group();
404 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
406 /* the basis is a member of an active route group, with the appropriate
407 properties; find other members */
409 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
410 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
411 if (v && v->route()->route_group() == group) {
418 uint32_t const sz = tracks.size ();
420 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
425 /** Call a slot for a given `basis' track and also for any track that is in the same
426 * active route group with a particular set of properties.
428 * @param sl Slot to call.
429 * @param basis Basis track.
430 * @param prop Properties that active edit groups must share to be included in the map.
434 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
436 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
437 set<boost::shared_ptr<Playlist> > playlists;
439 if (route_basis == 0) {
443 set<RouteTimeAxisView*> tracks;
444 tracks.insert (route_basis);
446 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
448 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
450 /* the basis is a member of an active route group, with the appropriate
451 properties; find other members */
453 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
454 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
456 if (v && v->route()->route_group() == group) {
458 boost::shared_ptr<Track> t = v->track();
460 if (playlists.insert (t->playlist()).second) {
461 /* haven't seen this playlist yet */
465 /* not actually a "Track", but a timeaxis view that
466 we should mapover anyway.
475 uint32_t const sz = tracks.size ();
477 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
483 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
485 boost::shared_ptr<Playlist> pl;
486 vector<boost::shared_ptr<Region> > results;
488 boost::shared_ptr<Track> tr;
490 if ((tr = tv.track()) == 0) {
495 if (&tv == &basis->get_time_axis_view()) {
496 /* looking in same track as the original */
500 if ((pl = tr->playlist()) != 0) {
501 pl->get_equivalent_regions (basis->region(), results);
504 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
505 if ((marv = tv.view()->find_view (*ir)) != 0) {
506 all_equivs->push_back (marv);
512 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
514 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);
516 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
518 equivalent_regions.push_back (basis);
522 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
524 RegionSelection equivalent;
526 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
528 vector<RegionView*> eq;
530 mapover_tracks_with_unique_playlists (
531 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
532 &(*i)->get_time_axis_view(), prop);
534 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
545 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
547 int region_count = 0;
549 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
551 RouteTimeAxisView* tatv;
553 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
555 boost::shared_ptr<Playlist> pl;
556 vector<boost::shared_ptr<Region> > results;
558 boost::shared_ptr<Track> tr;
560 if ((tr = tatv->track()) == 0) {
565 if ((pl = (tr->playlist())) != 0) {
566 pl->get_region_list_equivalent_regions (region, results);
569 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
570 if ((marv = tatv->view()->find_view (*ir)) != 0) {
583 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
585 vector<RegionView*> all_equivalent_regions;
588 if (!clicked_regionview || !clicked_routeview) {
593 button_release_can_deselect = false;
596 if (op == Selection::Toggle || op == Selection::Set) {
599 case Selection::Toggle:
600 if (selection->selected (clicked_regionview)) {
603 /* whatever was clicked was selected already; do nothing here but allow
604 the button release to deselect it
607 button_release_can_deselect = true;
610 if (button_release_can_deselect) {
612 /* just remove this one region, but only on a permitted button release */
614 selection->remove (clicked_regionview);
617 /* no more deselect action on button release till a new press
618 finds an already selected object.
621 button_release_can_deselect = false;
629 if (selection->selected (clicked_routeview)) {
630 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
632 all_equivalent_regions.push_back (clicked_regionview);
635 /* add all the equivalent regions, but only on button press */
637 if (!all_equivalent_regions.empty()) {
641 selection->add (all_equivalent_regions);
647 if (!selection->selected (clicked_regionview)) {
648 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
649 selection->set (all_equivalent_regions);
652 /* clicked on an already selected region */
656 if (selection->regions.size() > 1) {
657 /* collapse region selection down to just this one region (and its equivalents) */
658 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
659 selection->set(all_equivalent_regions);
671 } else if (op == Selection::Extend) {
673 list<Selectable*> results;
674 framepos_t last_frame;
675 framepos_t first_frame;
676 bool same_track = false;
678 /* 1. find the last selected regionview in the track that was clicked in */
681 first_frame = max_framepos;
683 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
684 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
686 if ((*x)->region()->last_frame() > last_frame) {
687 last_frame = (*x)->region()->last_frame();
690 if ((*x)->region()->first_frame() < first_frame) {
691 first_frame = (*x)->region()->first_frame();
700 /* 2. figure out the boundaries for our search for new objects */
702 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
703 case Evoral::OverlapNone:
704 if (last_frame < clicked_regionview->region()->first_frame()) {
705 first_frame = last_frame;
706 last_frame = clicked_regionview->region()->last_frame();
708 last_frame = first_frame;
709 first_frame = clicked_regionview->region()->first_frame();
713 case Evoral::OverlapExternal:
714 if (last_frame < clicked_regionview->region()->first_frame()) {
715 first_frame = last_frame;
716 last_frame = clicked_regionview->region()->last_frame();
718 last_frame = first_frame;
719 first_frame = clicked_regionview->region()->first_frame();
723 case Evoral::OverlapInternal:
724 if (last_frame < clicked_regionview->region()->first_frame()) {
725 first_frame = last_frame;
726 last_frame = clicked_regionview->region()->last_frame();
728 last_frame = first_frame;
729 first_frame = clicked_regionview->region()->first_frame();
733 case Evoral::OverlapStart:
734 case Evoral::OverlapEnd:
735 /* nothing to do except add clicked region to selection, since it
736 overlaps with the existing selection in this track.
743 /* click in a track that has no regions selected, so extend vertically
744 to pick out all regions that are defined by the existing selection
749 first_frame = clicked_regionview->region()->position();
750 last_frame = clicked_regionview->region()->last_frame();
752 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
753 if ((*i)->region()->position() < first_frame) {
754 first_frame = (*i)->region()->position();
756 if ((*i)->region()->last_frame() + 1 > last_frame) {
757 last_frame = (*i)->region()->last_frame();
762 /* 2. find all the tracks we should select in */
764 set<RouteTimeAxisView*> relevant_tracks;
766 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
767 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
769 relevant_tracks.insert (r);
773 set<RouteTimeAxisView*> already_in_selection;
775 if (relevant_tracks.empty()) {
777 /* no tracks selected .. thus .. if the
778 regionview we're in isn't selected
779 (i.e. we're about to extend to it), then
780 find all tracks between the this one and
784 if (!selection->selected (clicked_regionview)) {
786 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
790 /* add this track to the ones we will search */
792 relevant_tracks.insert (rtv);
794 /* find the track closest to this one that
795 already a selected region.
798 RouteTimeAxisView* closest = 0;
799 int distance = INT_MAX;
800 int key = rtv->route()->order_key ();
802 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
804 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
806 if (artv && artv != rtv) {
808 pair<set<RouteTimeAxisView*>::iterator,bool> result;
810 result = already_in_selection.insert (artv);
813 /* newly added to already_in_selection */
815 int d = artv->route()->order_key ();
819 if (abs (d) < distance) {
829 /* now add all tracks between that one and this one */
831 int okey = closest->route()->order_key ();
837 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
838 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
839 if (artv && artv != rtv) {
841 int k = artv->route()->order_key ();
843 if (k >= okey && k <= key) {
845 /* in range but don't add it if
846 it already has tracks selected.
847 this avoids odd selection
848 behaviour that feels wrong.
851 if (find (already_in_selection.begin(),
852 already_in_selection.end(),
853 artv) == already_in_selection.end()) {
855 relevant_tracks.insert (artv);
865 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
866 one that was clicked.
869 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
870 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
873 /* 4. convert to a vector of regions */
875 vector<RegionView*> regions;
877 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
880 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
881 regions.push_back (arv);
885 if (!regions.empty()) {
886 selection->add (regions);
897 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
899 vector<RegionView*> all_equivalent_regions;
901 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
903 if (all_equivalent_regions.empty()) {
907 begin_reversible_selection_op (X_("set selected regions"));
910 case Selection::Toggle:
911 /* XXX this is not correct */
912 selection->toggle (all_equivalent_regions);
915 selection->set (all_equivalent_regions);
917 case Selection::Extend:
918 selection->add (all_equivalent_regions);
921 selection->add (all_equivalent_regions);
925 commit_reversible_selection_op () ;
929 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
932 boost::shared_ptr<Region> r (weak_r.lock());
938 if ((rv = sv->find_view (r)) == 0) {
942 /* don't reset the selection if its something other than
943 a single other region.
946 if (selection->regions.size() > 1) {
950 begin_reversible_selection_op (X_("set selected regions"));
954 commit_reversible_selection_op () ;
960 Editor::track_selection_changed ()
962 switch (selection->tracks.size()) {
966 set_selected_mixer_strip (*(selection->tracks.front()));
970 RouteNotificationListPtr routes (new RouteNotificationList);
972 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
974 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
976 (*i)->set_selected (yn);
978 TimeAxisView::Children c = (*i)->get_child_list ();
979 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
980 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
984 (*i)->reshow_selection (selection->time);
986 (*i)->hide_selection ();
991 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
993 routes->push_back (rtav->route());
998 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1000 /* notify control protocols */
1002 ControlProtocol::TrackSelectionChanged (routes);
1006 Editor::time_selection_changed ()
1008 if (Profile->get_sae()) {
1012 /* XXX this is superficially inefficient. Hide the selection in all
1013 * tracks, then show it in all selected tracks.
1015 * However, if you investigate what this actually does, it isn't
1016 * anywhere nearly as bad as it may appear. Remember: nothing is
1017 * redrawn or even recomputed during these two loops - that only
1018 * happens when we next render ...
1021 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1022 (*i)->hide_selection ();
1025 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1026 (*i)->show_selection (selection->time);
1029 if (selection->time.empty()) {
1030 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1032 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1035 /* propagate into backend, but only when there is no drag or we are at
1036 * the end of a drag, otherwise this is too expensive (could case a
1037 * locate per mouse motion event.
1040 if (_session && !_drags->active()) {
1041 if (selection->time.length() != 0) {
1042 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1044 _session->clear_range_selection ();
1049 /** Set all region actions to have a given sensitivity */
1051 Editor::sensitize_all_region_actions (bool s)
1053 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1055 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1056 (*i)->set_sensitive (s);
1059 _all_region_actions_sensitized = s;
1062 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1063 * This method should be called just before displaying a Region menu. When a Region menu is not
1064 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1065 * on entered_regionviews work without having to check sensitivity every time the selection or
1066 * entered_regionview changes.
1068 * This method also sets up toggle action state as appropriate.
1071 Editor::sensitize_the_right_region_actions ()
1074 RegionSelection rs = get_regions_from_selection_and_entered ();
1075 sensitize_all_region_actions (!rs.empty ());
1077 _ignore_region_action = true;
1079 /* Look through the regions that are selected and make notes about what we have got */
1081 bool have_audio = false;
1082 bool have_multichannel_audio = false;
1083 bool have_midi = false;
1084 bool have_locked = false;
1085 bool have_unlocked = false;
1086 bool have_video_locked = false;
1087 bool have_video_unlocked = false;
1088 bool have_position_lock_style_audio = false;
1089 bool have_position_lock_style_music = false;
1090 bool have_muted = false;
1091 bool have_unmuted = false;
1092 bool have_opaque = false;
1093 bool have_non_opaque = false;
1094 bool have_not_at_natural_position = false;
1095 bool have_envelope_active = false;
1096 bool have_envelope_inactive = false;
1097 bool have_non_unity_scale_amplitude = false;
1098 bool have_compound_regions = false;
1099 bool have_inactive_fade_in = false;
1100 bool have_inactive_fade_out = false;
1101 bool have_active_fade_in = false;
1102 bool have_active_fade_out = false;
1104 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1106 boost::shared_ptr<Region> r = (*i)->region ();
1107 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1111 if (ar->n_channels() > 1) {
1112 have_multichannel_audio = true;
1116 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1120 if (r->is_compound()) {
1121 have_compound_regions = true;
1127 have_unlocked = true;
1130 if (r->video_locked()) {
1131 have_video_locked = true;
1133 have_video_unlocked = true;
1136 if (r->position_lock_style() == MusicTime) {
1137 have_position_lock_style_music = true;
1139 have_position_lock_style_audio = true;
1145 have_unmuted = true;
1151 have_non_opaque = true;
1154 if (!r->at_natural_position()) {
1155 have_not_at_natural_position = true;
1159 if (ar->envelope_active()) {
1160 have_envelope_active = true;
1162 have_envelope_inactive = true;
1165 if (ar->scale_amplitude() != 1) {
1166 have_non_unity_scale_amplitude = true;
1169 if (ar->fade_in_active ()) {
1170 have_active_fade_in = true;
1172 have_inactive_fade_in = true;
1175 if (ar->fade_out_active ()) {
1176 have_active_fade_out = true;
1178 have_inactive_fade_out = true;
1183 if (rs.size() > 1) {
1184 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1185 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1186 _region_actions->get_action("rename-region")->set_sensitive (false);
1188 /* XXX need to check whether there is than 1 per
1189 playlist, because otherwise this makes no sense.
1191 _region_actions->get_action("combine-regions")->set_sensitive (true);
1193 _region_actions->get_action("combine-regions")->set_sensitive (false);
1195 } else if (rs.size() == 1) {
1196 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1197 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1198 _region_actions->get_action("combine-regions")->set_sensitive (false);
1201 if (!have_multichannel_audio) {
1202 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1206 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1207 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1208 _region_actions->get_action("quantize-region")->set_sensitive (false);
1209 _region_actions->get_action("legatize-region")->set_sensitive (false);
1210 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1211 _region_actions->get_action("fork-region")->set_sensitive (false);
1212 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1213 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1214 _region_actions->get_action("transpose-region")->set_sensitive (false);
1216 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1217 /* others were already marked sensitive */
1220 if (_edit_point == EditAtMouse) {
1221 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1222 _region_actions->get_action("trim-front")->set_sensitive (false);
1223 _region_actions->get_action("trim-back")->set_sensitive (false);
1224 _region_actions->get_action("split-region")->set_sensitive (false);
1225 _region_actions->get_action("place-transient")->set_sensitive (false);
1228 if (have_compound_regions) {
1229 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1231 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1236 if (have_envelope_active && !have_envelope_inactive) {
1237 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1238 } else if (have_envelope_active && have_envelope_inactive) {
1239 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1244 _region_actions->get_action("analyze-region")->set_sensitive (false);
1245 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1246 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1247 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1251 if (!have_non_unity_scale_amplitude || !have_audio) {
1252 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1255 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1256 a->set_active (have_locked && !have_unlocked);
1257 if (have_locked && have_unlocked) {
1258 // a->set_inconsistent ();
1261 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1262 a->set_active (have_video_locked && !have_video_unlocked);
1263 if (have_video_locked && have_video_unlocked) {
1264 // a->set_inconsistent ();
1267 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1268 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1270 if (have_position_lock_style_music && have_position_lock_style_audio) {
1271 // a->set_inconsistent ();
1274 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1275 a->set_active (have_muted && !have_unmuted);
1276 if (have_muted && have_unmuted) {
1277 // a->set_inconsistent ();
1280 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1281 a->set_active (have_opaque && !have_non_opaque);
1282 if (have_opaque && have_non_opaque) {
1283 // a->set_inconsistent ();
1286 if (!have_not_at_natural_position) {
1287 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1290 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1291 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1292 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1294 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1297 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1298 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1299 if (have_active_fade_in && have_inactive_fade_in) {
1300 // a->set_inconsistent ();
1303 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1304 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1306 if (have_active_fade_out && have_inactive_fade_out) {
1307 // a->set_inconsistent ();
1310 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1311 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1313 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1314 a->set_active (have_active_fade && !have_inactive_fade);
1316 if (have_active_fade && have_inactive_fade) {
1317 // a->set_inconsistent ();
1320 _ignore_region_action = false;
1322 _all_region_actions_sensitized = false;
1327 Editor::region_selection_changed ()
1329 _regions->block_change_connection (true);
1330 editor_regions_selection_changed_connection.block(true);
1332 if (_region_selection_change_updates_region_list) {
1333 _regions->unselect_all ();
1336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1337 (*i)->set_selected_regionviews (selection->regions);
1340 if (_region_selection_change_updates_region_list) {
1341 _regions->set_selected (selection->regions);
1344 _regions->block_change_connection (false);
1345 editor_regions_selection_changed_connection.block(false);
1347 if (!_all_region_actions_sensitized) {
1348 /* This selection change might have changed what region actions
1349 are allowed, so sensitize them all in case a key is pressed.
1351 sensitize_all_region_actions (true);
1354 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1355 maybe_locate_with_edit_preroll (selection->regions.start());
1358 /* propagate into backend */
1361 if (!selection->regions.empty()) {
1362 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1364 _session->clear_object_selection ();
1371 Editor::point_selection_changed ()
1373 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1374 (*i)->set_selected_points (selection->points);
1379 Editor::select_all_in_track (Selection::Operation op)
1381 list<Selectable *> touched;
1383 if (!clicked_routeview) {
1387 begin_reversible_selection_op (X_("Select All in Track"));
1389 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1392 case Selection::Toggle:
1393 selection->add (touched);
1395 case Selection::Set:
1396 selection->set (touched);
1398 case Selection::Extend:
1399 /* meaningless, because we're selecting everything */
1401 case Selection::Add:
1402 selection->add (touched);
1406 commit_reversible_selection_op ();
1410 Editor::select_all_internal_edit (Selection::Operation)
1412 bool selected = false;
1414 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1415 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1417 mrv->select_all_notes ();
1422 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1424 mrv->select_all_notes ();
1432 Editor::select_all_objects (Selection::Operation op)
1434 list<Selectable *> touched;
1436 TrackViewList ts = track_views;
1438 if (internal_editing() && select_all_internal_edit(op)) {
1439 return; // Selected notes
1442 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1443 if ((*iter)->hidden()) {
1446 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1447 selection->add (*iter);
1451 begin_reversible_selection_op (X_("select all"));
1453 case Selection::Add:
1454 selection->add (touched);
1456 case Selection::Toggle:
1457 selection->add (touched);
1459 case Selection::Set:
1460 selection->set (touched);
1462 case Selection::Extend:
1463 /* meaningless, because we're selecting everything */
1466 commit_reversible_selection_op ();
1470 Editor::invert_selection_in_track ()
1472 list<Selectable *> touched;
1474 if (!clicked_routeview) {
1478 begin_reversible_selection_op (X_("Invert Selection in Track"));
1479 clicked_routeview->get_inverted_selectables (*selection, touched);
1480 selection->set (touched);
1481 commit_reversible_selection_op ();
1485 Editor::invert_selection ()
1487 list<Selectable *> touched;
1489 if (internal_editing()) {
1490 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1491 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1493 mrv->invert_selection ();
1499 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1500 if ((*iter)->hidden()) {
1503 (*iter)->get_inverted_selectables (*selection, touched);
1506 begin_reversible_selection_op (X_("Invert Selection"));
1507 selection->set (touched);
1508 commit_reversible_selection_op ();
1511 /** @param start Start time in session frames.
1512 * @param end End time in session frames.
1513 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1514 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1515 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1516 * within the region are already selected.
1519 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1521 list<Selectable*> found;
1523 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1525 if ((*iter)->hidden()) {
1529 (*iter)->get_selectables (start, end, top, bot, found);
1532 if (found.empty()) {
1533 selection->clear_objects();
1534 selection->clear_time ();
1538 if (preserve_if_selected && op != Selection::Toggle) {
1539 list<Selectable*>::iterator i = found.begin();
1540 while (i != found.end() && (*i)->get_selected()) {
1544 if (i == found.end()) {
1549 begin_reversible_selection_op (X_("select all within"));
1551 case Selection::Add:
1552 selection->add (found);
1554 case Selection::Toggle:
1555 selection->toggle (found);
1557 case Selection::Set:
1558 selection->set (found);
1560 case Selection::Extend:
1561 /* not defined yet */
1565 commit_reversible_selection_op ();
1569 Editor::set_selection_from_region ()
1571 if (selection->regions.empty()) {
1575 /* find all the tracks that have selected regions */
1577 set<TimeAxisView*> tracks;
1579 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1580 tracks.insert (&(*r)->get_time_axis_view());
1584 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1586 /* select range (this will clear the region selection) */
1588 selection->set (selection->regions.start(), selection->regions.end_frame());
1590 /* and select the tracks */
1592 selection->set (tvl);
1594 if (!Profile->get_sae()) {
1595 set_mouse_mode (Editing::MouseRange, false);
1600 Editor::set_selection_from_punch()
1604 if ((location = _session->locations()->auto_punch_location()) == 0) {
1608 set_selection_from_range (*location);
1612 Editor::set_selection_from_loop()
1616 if ((location = _session->locations()->auto_loop_location()) == 0) {
1619 set_selection_from_range (*location);
1623 Editor::set_selection_from_range (Location& loc)
1625 begin_reversible_selection_op (X_("set selection from range"));
1626 selection->set (loc.start(), loc.end());
1627 commit_reversible_selection_op ();
1629 if (!Profile->get_sae()) {
1630 set_mouse_mode (Editing::MouseRange, false);
1635 Editor::select_all_selectables_using_time_selection ()
1637 list<Selectable *> touched;
1639 if (selection->time.empty()) {
1643 framepos_t start = selection->time[clicked_selection].start;
1644 framepos_t end = selection->time[clicked_selection].end;
1646 if (end - start < 1) {
1652 if (selection->tracks.empty()) {
1655 ts = &selection->tracks;
1658 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1659 if ((*iter)->hidden()) {
1662 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1665 begin_reversible_selection_op (X_("select all from range"));
1666 selection->set (touched);
1667 commit_reversible_selection_op ();
1672 Editor::select_all_selectables_using_punch()
1674 Location* location = _session->locations()->auto_punch_location();
1675 list<Selectable *> touched;
1677 if (location == 0 || (location->end() - location->start() <= 1)) {
1684 if (selection->tracks.empty()) {
1687 ts = &selection->tracks;
1690 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1691 if ((*iter)->hidden()) {
1694 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1696 begin_reversible_selection_op (X_("select all from punch"));
1697 selection->set (touched);
1698 commit_reversible_selection_op ();
1703 Editor::select_all_selectables_using_loop()
1705 Location* location = _session->locations()->auto_loop_location();
1706 list<Selectable *> touched;
1708 if (location == 0 || (location->end() - location->start() <= 1)) {
1715 if (selection->tracks.empty()) {
1718 ts = &selection->tracks;
1721 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1722 if ((*iter)->hidden()) {
1725 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1727 begin_reversible_selection_op (X_("select all from loop"));
1728 selection->set (touched);
1729 commit_reversible_selection_op ();
1734 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1738 list<Selectable *> touched;
1741 start = cursor->current_frame();
1742 end = _session->current_end_frame();
1744 if (cursor->current_frame() > 0) {
1746 end = cursor->current_frame() - 1;
1752 if (internal_editing()) {
1753 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1754 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1756 mrv->select_range (start, end);
1763 begin_reversible_selection_op (X_("select all after cursor"));
1765 begin_reversible_selection_op (X_("select all before cursor"));
1770 if (selection->tracks.empty()) {
1773 ts = &selection->tracks;
1776 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1777 if ((*iter)->hidden()) {
1780 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1782 selection->set (touched);
1783 commit_reversible_selection_op ();
1787 Editor::select_all_selectables_using_edit (bool after)
1791 list<Selectable *> touched;
1794 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1795 end = _session->current_end_frame();
1797 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1805 if (internal_editing()) {
1806 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1807 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1808 mrv->select_range (start, end);
1814 begin_reversible_selection_op (X_("select all after edit"));
1816 begin_reversible_selection_op (X_("select all before edit"));
1821 if (selection->tracks.empty()) {
1824 ts = &selection->tracks;
1827 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1828 if ((*iter)->hidden()) {
1831 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1833 selection->set (touched);
1834 commit_reversible_selection_op ();
1838 Editor::select_all_selectables_between (bool within)
1842 list<Selectable *> touched;
1844 if (!get_edit_op_range (start, end)) {
1848 if (internal_editing()) {
1849 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1850 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1851 mrv->select_range (start, end);
1858 if (selection->tracks.empty()) {
1861 ts = &selection->tracks;
1864 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1865 if ((*iter)->hidden()) {
1868 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1871 begin_reversible_selection_op (X_("Select all Selectables Between"));
1872 selection->set (touched);
1873 commit_reversible_selection_op ();
1877 Editor::select_range_between ()
1882 if ( !selection->time.empty() ) {
1883 selection->clear_time ();
1886 if (!get_edit_op_range (start, end)) {
1890 begin_reversible_selection_op (X_("Select Range Between"));
1891 set_mouse_mode (MouseRange);
1892 selection->set (start, end);
1893 commit_reversible_selection_op ();
1897 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1902 /* if an explicit range exists, use it */
1904 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1905 /* we know that these are ordered */
1906 start = selection->time.start();
1907 end = selection->time.end_frame();
1915 // if (!mouse_frame (m, ignored)) {
1916 // /* mouse is not in a canvas, try playhead+selected marker.
1917 // this is probably most true when using menus.
1920 // if (selection->markers.empty()) {
1924 // start = selection->markers.front()->position();
1925 // end = _session->audible_frame();
1929 // switch (_edit_point) {
1930 // case EditAtPlayhead:
1931 // if (selection->markers.empty()) {
1932 // /* use mouse + playhead */
1934 // end = _session->audible_frame();
1936 // /* use playhead + selected marker */
1937 // start = _session->audible_frame();
1938 // end = selection->markers.front()->position();
1942 // case EditAtMouse:
1943 // /* use mouse + selected marker */
1944 // if (selection->markers.empty()) {
1946 // end = _session->audible_frame();
1948 // start = selection->markers.front()->position();
1953 // case EditAtSelectedMarker:
1954 // /* use mouse + selected marker */
1955 // if (selection->markers.empty()) {
1957 // MessageDialog win (_("No edit range defined"),
1962 // win.set_secondary_text (
1963 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1966 // win.set_default_response (RESPONSE_CLOSE);
1967 // win.set_position (Gtk::WIN_POS_MOUSE);
1972 // return false; // NO RANGE
1974 // start = selection->markers.front()->position();
1980 // if (start == end) {
1984 // if (start > end) {
1985 // swap (start, end);
1988 /* turn range into one delimited by start...end,
1998 Editor::deselect_all ()
2000 begin_reversible_selection_op (X_("Deselect All"));
2001 selection->clear ();
2002 commit_reversible_selection_op ();
2006 Editor::select_range (framepos_t s, framepos_t e)
2008 begin_reversible_selection_op (X_("Select Range"));
2009 selection->add (clicked_axisview);
2010 selection->time.clear ();
2011 long ret = selection->set (s, e);
2012 commit_reversible_selection_op ();