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"
35 #include "audio_time_axis.h"
36 #include "audio_region_view.h"
37 #include "audio_streamview.h"
38 #include "automation_line.h"
39 #include "control_point.h"
40 #include "editor_regions.h"
41 #include "editor_cursors.h"
42 #include "midi_region_view.h"
47 using namespace ARDOUR;
51 using namespace Gtkmm2ext;
52 using namespace Editing;
54 struct TrackViewByPositionSorter
56 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
57 return a->y_position() < b->y_position();
62 Editor::extend_selection_to_track (TimeAxisView& view)
64 if (selection->selected (&view)) {
65 /* already selected, do nothing */
69 if (selection->tracks.empty()) {
71 if (!selection->selected (&view)) {
72 selection->set (&view);
79 /* something is already selected, so figure out which range of things to add */
81 TrackViewList to_be_added;
82 TrackViewList sorted = track_views;
83 TrackViewByPositionSorter cmp;
84 bool passed_clicked = false;
89 if (!selection->selected (&view)) {
90 to_be_added.push_back (&view);
93 /* figure out if we should go forward or backwards */
95 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
98 passed_clicked = true;
101 if (selection->selected (*i)) {
102 if (passed_clicked) {
111 passed_clicked = false;
115 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
118 passed_clicked = true;
122 if (passed_clicked) {
123 if ((*i)->hidden()) {
126 if (selection->selected (*i)) {
128 } else if (!(*i)->hidden()) {
129 to_be_added.push_back (*i);
136 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
139 passed_clicked = true;
143 if (passed_clicked) {
145 if ((*r)->hidden()) {
149 if (selection->selected (*r)) {
151 } else if (!(*r)->hidden()) {
152 to_be_added.push_back (*r);
158 if (!to_be_added.empty()) {
159 selection->add (to_be_added);
167 Editor::select_all_tracks ()
169 TrackViewList visible_views;
170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
171 if ((*i)->marked_for_display()) {
172 visible_views.push_back (*i);
175 selection->set (visible_views);
178 /** Select clicked_axisview, unless there are no currently selected
179 * tracks, in which case nothing will happen unless `force' is true.
182 Editor::set_selected_track_as_side_effect (Selection::Operation op)
184 if (!clicked_axisview) {
188 RouteGroup* group = NULL;
189 if (clicked_routeview) {
190 group = clicked_routeview->route()->route_group();
193 bool had_tracks = !selection->tracks.empty();
194 RouteGroup& arg (_session->all_route_group());
197 case Selection::Toggle:
198 if (selection->selected (clicked_axisview)) {
199 if (arg.is_select() && arg.is_active()) {
200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
201 selection->remove(*i);
203 } else if (group && group->is_active()) {
204 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
205 if ((*i)->route_group() == group) {
206 selection->remove(*i);
210 selection->remove (clicked_axisview);
213 if (arg.is_select() && arg.is_active()) {
214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217 } else if (group && group->is_active()) {
218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 if ((*i)->route_group() == group) {
224 selection->add (clicked_axisview);
230 if (!had_tracks && arg.is_select() && arg.is_active()) {
231 /* nothing was selected already, and all group is active etc. so use
234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
237 } else if (group && group->is_active()) {
238 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
239 if ((*i)->route_group() == group) {
244 selection->add (clicked_axisview);
250 if (!had_tracks && arg.is_select() && arg.is_active()) {
251 /* nothing was selected already, and all group is active etc. so use
254 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
257 } else if (group && group->is_active()) {
258 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
259 if ((*i)->route_group() == group) {
264 selection->set (clicked_axisview);
268 case Selection::Extend:
275 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
277 begin_reversible_selection_op (X_("Set Selected Track"));
280 case Selection::Toggle:
281 if (selection->selected (&view)) {
283 selection->remove (&view);
286 selection->add (&view);
291 if (!selection->selected (&view)) {
292 selection->add (&view);
297 selection->set (&view);
300 case Selection::Extend:
301 extend_selection_to_track (view);
305 commit_reversible_selection_op ();
309 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
311 if (!clicked_routeview) {
319 set_selected_track (*clicked_routeview, op, no_remove);
323 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
325 if (!clicked_control_point) {
332 selection->set (clicked_control_point);
337 selection->add (clicked_control_point);
340 case Selection::Toggle:
341 /* This is a bit of a hack; if we Primary-Click-Drag a control
342 point (for push drag) we want the point we clicked on to be
343 selected, otherwise we end up confusingly dragging an
344 unselected point. So here we ensure that the point is selected
345 after the press, and if we subsequently get a release (meaning no
346 drag occurred) we set things up so that the toggle has happened.
348 if (press && !selection->selected (clicked_control_point)) {
349 /* This is the button press, and the control point is not selected; make it so,
350 in case this press leads to a drag. Also note that having done this, we don't
351 need to toggle again on release.
353 selection->toggle (clicked_control_point);
354 _control_point_toggled_on_press = true;
355 } else if (!press && !_control_point_toggled_on_press) {
356 /* This is the release, and the point wasn't toggled on the press, so do it now */
357 selection->toggle (clicked_control_point);
360 _control_point_toggled_on_press = false;
363 case Selection::Extend:
372 Editor::get_onscreen_tracks (TrackViewList& tvl)
374 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
375 if ((*i)->y_position() < _visible_canvas_height) {
381 /** Call a slot for a given `basis' track and also for any track that is in the same
382 * active route group with a particular set of properties.
384 * @param sl Slot to call.
385 * @param basis Basis track.
386 * @param prop Properties that active edit groups must share to be included in the map.
390 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
392 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
394 if (route_basis == 0) {
398 set<RouteTimeAxisView*> tracks;
399 tracks.insert (route_basis);
401 RouteGroup* group = route_basis->route()->route_group();
403 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
405 /* the basis is a member of an active route group, with the appropriate
406 properties; find other members */
408 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
409 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
410 if (v && v->route()->route_group() == group) {
417 uint32_t const sz = tracks.size ();
419 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
424 /** Call a slot for a given `basis' track and also for any track that is in the same
425 * active route group with a particular set of properties.
427 * @param sl Slot to call.
428 * @param basis Basis track.
429 * @param prop Properties that active edit groups must share to be included in the map.
433 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
435 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
436 set<boost::shared_ptr<Playlist> > playlists;
438 if (route_basis == 0) {
442 set<RouteTimeAxisView*> tracks;
443 tracks.insert (route_basis);
445 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
447 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
449 /* the basis is a member of an active route group, with the appropriate
450 properties; find other members */
452 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
453 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
455 if (v && v->route()->route_group() == group) {
457 boost::shared_ptr<Track> t = v->track();
459 if (playlists.insert (t->playlist()).second) {
460 /* haven't seen this playlist yet */
464 /* not actually a "Track", but a timeaxis view that
465 we should mapover anyway.
474 uint32_t const sz = tracks.size ();
476 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
482 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
484 boost::shared_ptr<Playlist> pl;
485 vector<boost::shared_ptr<Region> > results;
487 boost::shared_ptr<Track> tr;
489 if ((tr = tv.track()) == 0) {
494 if (&tv == &basis->get_time_axis_view()) {
495 /* looking in same track as the original */
499 if ((pl = tr->playlist()) != 0) {
500 pl->get_equivalent_regions (basis->region(), results);
503 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
504 if ((marv = tv.view()->find_view (*ir)) != 0) {
505 all_equivs->push_back (marv);
511 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
513 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);
515 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
517 equivalent_regions.push_back (basis);
521 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
523 RegionSelection equivalent;
525 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
527 vector<RegionView*> eq;
529 mapover_tracks_with_unique_playlists (
530 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
531 &(*i)->get_time_axis_view(), prop);
533 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
544 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
546 int region_count = 0;
548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
550 RouteTimeAxisView* tatv;
552 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
554 boost::shared_ptr<Playlist> pl;
555 vector<boost::shared_ptr<Region> > results;
557 boost::shared_ptr<Track> tr;
559 if ((tr = tatv->track()) == 0) {
564 if ((pl = (tr->playlist())) != 0) {
565 pl->get_region_list_equivalent_regions (region, results);
568 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
569 if ((marv = tatv->view()->find_view (*ir)) != 0) {
582 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
584 vector<RegionView*> all_equivalent_regions;
587 if (!clicked_regionview || !clicked_routeview) {
592 button_release_can_deselect = false;
595 if (op == Selection::Toggle || op == Selection::Set) {
598 case Selection::Toggle:
599 if (selection->selected (clicked_regionview)) {
602 /* whatever was clicked was selected already; do nothing here but allow
603 the button release to deselect it
606 button_release_can_deselect = true;
609 if (button_release_can_deselect) {
611 /* just remove this one region, but only on a permitted button release */
613 selection->remove (clicked_regionview);
616 /* no more deselect action on button release till a new press
617 finds an already selected object.
620 button_release_can_deselect = false;
628 if (selection->selected (clicked_routeview)) {
629 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
631 all_equivalent_regions.push_back (clicked_regionview);
634 /* add all the equivalent regions, but only on button press */
636 if (!all_equivalent_regions.empty()) {
640 selection->add (all_equivalent_regions);
646 if (!selection->selected (clicked_regionview)) {
647 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
648 selection->set (all_equivalent_regions);
651 /* clicked on an already selected region */
655 if (selection->regions.size() > 1) {
656 /* collapse region selection down to just this one region (and its equivalents) */
657 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
658 selection->set(all_equivalent_regions);
670 } else if (op == Selection::Extend) {
672 list<Selectable*> results;
673 framepos_t last_frame;
674 framepos_t first_frame;
675 bool same_track = false;
677 /* 1. find the last selected regionview in the track that was clicked in */
680 first_frame = max_framepos;
682 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
683 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
685 if ((*x)->region()->last_frame() > last_frame) {
686 last_frame = (*x)->region()->last_frame();
689 if ((*x)->region()->first_frame() < first_frame) {
690 first_frame = (*x)->region()->first_frame();
699 /* 2. figure out the boundaries for our search for new objects */
701 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
702 case Evoral::OverlapNone:
703 if (last_frame < clicked_regionview->region()->first_frame()) {
704 first_frame = last_frame;
705 last_frame = clicked_regionview->region()->last_frame();
707 last_frame = first_frame;
708 first_frame = clicked_regionview->region()->first_frame();
712 case Evoral::OverlapExternal:
713 if (last_frame < clicked_regionview->region()->first_frame()) {
714 first_frame = last_frame;
715 last_frame = clicked_regionview->region()->last_frame();
717 last_frame = first_frame;
718 first_frame = clicked_regionview->region()->first_frame();
722 case Evoral::OverlapInternal:
723 if (last_frame < clicked_regionview->region()->first_frame()) {
724 first_frame = last_frame;
725 last_frame = clicked_regionview->region()->last_frame();
727 last_frame = first_frame;
728 first_frame = clicked_regionview->region()->first_frame();
732 case Evoral::OverlapStart:
733 case Evoral::OverlapEnd:
734 /* nothing to do except add clicked region to selection, since it
735 overlaps with the existing selection in this track.
742 /* click in a track that has no regions selected, so extend vertically
743 to pick out all regions that are defined by the existing selection
748 first_frame = clicked_regionview->region()->position();
749 last_frame = clicked_regionview->region()->last_frame();
751 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
752 if ((*i)->region()->position() < first_frame) {
753 first_frame = (*i)->region()->position();
755 if ((*i)->region()->last_frame() + 1 > last_frame) {
756 last_frame = (*i)->region()->last_frame();
761 /* 2. find all the tracks we should select in */
763 set<RouteTimeAxisView*> relevant_tracks;
765 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
766 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
768 relevant_tracks.insert (r);
772 set<RouteTimeAxisView*> already_in_selection;
774 if (relevant_tracks.empty()) {
776 /* no tracks selected .. thus .. if the
777 regionview we're in isn't selected
778 (i.e. we're about to extend to it), then
779 find all tracks between the this one and
783 if (!selection->selected (clicked_regionview)) {
785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
789 /* add this track to the ones we will search */
791 relevant_tracks.insert (rtv);
793 /* find the track closest to this one that
794 already a selected region.
797 RouteTimeAxisView* closest = 0;
798 int distance = INT_MAX;
799 int key = rtv->route()->order_key ();
801 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
803 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
805 if (artv && artv != rtv) {
807 pair<set<RouteTimeAxisView*>::iterator,bool> result;
809 result = already_in_selection.insert (artv);
812 /* newly added to already_in_selection */
814 int d = artv->route()->order_key ();
818 if (abs (d) < distance) {
828 /* now add all tracks between that one and this one */
830 int okey = closest->route()->order_key ();
836 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
837 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
838 if (artv && artv != rtv) {
840 int k = artv->route()->order_key ();
842 if (k >= okey && k <= key) {
844 /* in range but don't add it if
845 it already has tracks selected.
846 this avoids odd selection
847 behaviour that feels wrong.
850 if (find (already_in_selection.begin(),
851 already_in_selection.end(),
852 artv) == already_in_selection.end()) {
854 relevant_tracks.insert (artv);
864 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
865 one that was clicked.
868 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
869 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
872 /* 4. convert to a vector of regions */
874 vector<RegionView*> regions;
876 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
879 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
880 regions.push_back (arv);
884 if (!regions.empty()) {
885 selection->add (regions);
896 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
898 vector<RegionView*> all_equivalent_regions;
900 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
902 if (all_equivalent_regions.empty()) {
906 begin_reversible_selection_op (X_("set selected regions"));
909 case Selection::Toggle:
910 /* XXX this is not correct */
911 selection->toggle (all_equivalent_regions);
914 selection->set (all_equivalent_regions);
916 case Selection::Extend:
917 selection->add (all_equivalent_regions);
920 selection->add (all_equivalent_regions);
924 commit_reversible_selection_op () ;
928 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
931 boost::shared_ptr<Region> r (weak_r.lock());
937 if ((rv = sv->find_view (r)) == 0) {
941 /* don't reset the selection if its something other than
942 a single other region.
945 if (selection->regions.size() > 1) {
949 begin_reversible_selection_op (X_("set selected regions"));
953 commit_reversible_selection_op () ;
959 Editor::track_selection_changed ()
961 switch (selection->tracks.size()) {
965 set_selected_mixer_strip (*(selection->tracks.front()));
969 RouteNotificationListPtr routes (new RouteNotificationList);
971 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
973 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
975 (*i)->set_selected (yn);
977 TimeAxisView::Children c = (*i)->get_child_list ();
978 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
979 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
983 (*i)->reshow_selection (selection->time);
985 (*i)->hide_selection ();
990 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
992 routes->push_back (rtav->route());
997 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
999 /* notify control protocols */
1001 ControlProtocol::TrackSelectionChanged (routes);
1005 Editor::time_selection_changed ()
1007 if (Profile->get_sae()) {
1011 /* XXX this is superficially inefficient. Hide the selection in all
1012 * tracks, then show it in all selected tracks.
1014 * However, if you investigate what this actually does, it isn't
1015 * anywhere nearly as bad as it may appear. Remember: nothing is
1016 * redrawn or even recomputed during these two loops - that only
1017 * happens when we next render ...
1020 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1021 (*i)->hide_selection ();
1024 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1025 (*i)->show_selection (selection->time);
1028 if (selection->time.empty()) {
1029 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1031 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1034 /* propagate into backend */
1037 if (selection->time.length() != 0) {
1038 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1040 _session->clear_range_selection ();
1045 /** Set all region actions to have a given sensitivity */
1047 Editor::sensitize_all_region_actions (bool s)
1049 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1051 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1052 (*i)->set_sensitive (s);
1055 _all_region_actions_sensitized = s;
1058 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1059 * This method should be called just before displaying a Region menu. When a Region menu is not
1060 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1061 * on entered_regionviews work without having to check sensitivity every time the selection or
1062 * entered_regionview changes.
1064 * This method also sets up toggle action state as appropriate.
1067 Editor::sensitize_the_right_region_actions ()
1070 RegionSelection rs = get_regions_from_selection_and_entered ();
1071 sensitize_all_region_actions (!rs.empty ());
1073 _ignore_region_action = true;
1075 /* Look through the regions that are selected and make notes about what we have got */
1077 bool have_audio = false;
1078 bool have_multichannel_audio = false;
1079 bool have_midi = false;
1080 bool have_locked = false;
1081 bool have_unlocked = false;
1082 bool have_video_locked = false;
1083 bool have_video_unlocked = false;
1084 bool have_position_lock_style_audio = false;
1085 bool have_position_lock_style_music = false;
1086 bool have_muted = false;
1087 bool have_unmuted = false;
1088 bool have_opaque = false;
1089 bool have_non_opaque = false;
1090 bool have_not_at_natural_position = false;
1091 bool have_envelope_active = false;
1092 bool have_envelope_inactive = false;
1093 bool have_non_unity_scale_amplitude = false;
1094 bool have_compound_regions = false;
1095 bool have_inactive_fade_in = false;
1096 bool have_inactive_fade_out = false;
1097 bool have_active_fade_in = false;
1098 bool have_active_fade_out = false;
1100 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1102 boost::shared_ptr<Region> r = (*i)->region ();
1103 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1107 if (ar->n_channels() > 1) {
1108 have_multichannel_audio = true;
1112 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1116 if (r->is_compound()) {
1117 have_compound_regions = true;
1123 have_unlocked = true;
1126 if (r->video_locked()) {
1127 have_video_locked = true;
1129 have_video_unlocked = true;
1132 if (r->position_lock_style() == MusicTime) {
1133 have_position_lock_style_music = true;
1135 have_position_lock_style_audio = true;
1141 have_unmuted = true;
1147 have_non_opaque = true;
1150 if (!r->at_natural_position()) {
1151 have_not_at_natural_position = true;
1155 if (ar->envelope_active()) {
1156 have_envelope_active = true;
1158 have_envelope_inactive = true;
1161 if (ar->scale_amplitude() != 1) {
1162 have_non_unity_scale_amplitude = true;
1165 if (ar->fade_in_active ()) {
1166 have_active_fade_in = true;
1168 have_inactive_fade_in = true;
1171 if (ar->fade_out_active ()) {
1172 have_active_fade_out = true;
1174 have_inactive_fade_out = true;
1179 if (rs.size() > 1) {
1180 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1181 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1182 _region_actions->get_action("rename-region")->set_sensitive (false);
1184 /* XXX need to check whether there is than 1 per
1185 playlist, because otherwise this makes no sense.
1187 _region_actions->get_action("combine-regions")->set_sensitive (true);
1189 _region_actions->get_action("combine-regions")->set_sensitive (false);
1191 } else if (rs.size() == 1) {
1192 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1193 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1194 _region_actions->get_action("combine-regions")->set_sensitive (false);
1197 if (!have_multichannel_audio) {
1198 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1202 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1203 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1204 _region_actions->get_action("quantize-region")->set_sensitive (false);
1205 _region_actions->get_action("legatize-region")->set_sensitive (false);
1206 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1207 _region_actions->get_action("fork-region")->set_sensitive (false);
1208 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1209 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1210 _region_actions->get_action("transpose-region")->set_sensitive (false);
1212 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1213 /* others were already marked sensitive */
1216 if (_edit_point == EditAtMouse) {
1217 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1218 _region_actions->get_action("trim-front")->set_sensitive (false);
1219 _region_actions->get_action("trim-back")->set_sensitive (false);
1220 _region_actions->get_action("split-region")->set_sensitive (false);
1221 _region_actions->get_action("place-transient")->set_sensitive (false);
1224 if (have_compound_regions) {
1225 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1227 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1232 if (have_envelope_active && !have_envelope_inactive) {
1233 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1234 } else if (have_envelope_active && have_envelope_inactive) {
1235 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1240 _region_actions->get_action("analyze-region")->set_sensitive (false);
1241 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1242 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1243 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1247 if (!have_non_unity_scale_amplitude || !have_audio) {
1248 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1251 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1252 a->set_active (have_locked && !have_unlocked);
1253 if (have_locked && have_unlocked) {
1254 // a->set_inconsistent ();
1257 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1258 a->set_active (have_video_locked && !have_video_unlocked);
1259 if (have_video_locked && have_video_unlocked) {
1260 // a->set_inconsistent ();
1263 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1264 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1266 if (have_position_lock_style_music && have_position_lock_style_audio) {
1267 // a->set_inconsistent ();
1270 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1271 a->set_active (have_muted && !have_unmuted);
1272 if (have_muted && have_unmuted) {
1273 // a->set_inconsistent ();
1276 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1277 a->set_active (have_opaque && !have_non_opaque);
1278 if (have_opaque && have_non_opaque) {
1279 // a->set_inconsistent ();
1282 if (!have_not_at_natural_position) {
1283 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1286 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1287 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1288 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1290 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1293 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1294 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1295 if (have_active_fade_in && have_inactive_fade_in) {
1296 // a->set_inconsistent ();
1299 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1300 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1302 if (have_active_fade_out && have_inactive_fade_out) {
1303 // a->set_inconsistent ();
1306 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1307 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1309 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1310 a->set_active (have_active_fade && !have_inactive_fade);
1312 if (have_active_fade && have_inactive_fade) {
1313 // a->set_inconsistent ();
1316 _ignore_region_action = false;
1318 _all_region_actions_sensitized = false;
1323 Editor::region_selection_changed ()
1325 _regions->block_change_connection (true);
1326 editor_regions_selection_changed_connection.block(true);
1328 if (_region_selection_change_updates_region_list) {
1329 _regions->unselect_all ();
1332 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1333 (*i)->set_selected_regionviews (selection->regions);
1336 if (_region_selection_change_updates_region_list) {
1337 _regions->set_selected (selection->regions);
1340 _regions->block_change_connection (false);
1341 editor_regions_selection_changed_connection.block(false);
1343 if (!_all_region_actions_sensitized) {
1344 /* This selection change might have changed what region actions
1345 are allowed, so sensitize them all in case a key is pressed.
1347 sensitize_all_region_actions (true);
1350 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1351 maybe_locate_with_edit_preroll (selection->regions.start());
1354 /* propagate into backend */
1357 if (!selection->regions.empty()) {
1358 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1360 _session->clear_object_selection ();
1367 Editor::point_selection_changed ()
1369 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1370 (*i)->set_selected_points (selection->points);
1375 Editor::select_all_in_track (Selection::Operation op)
1377 list<Selectable *> touched;
1379 if (!clicked_routeview) {
1383 begin_reversible_selection_op (X_("Select All in Track"));
1385 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1388 case Selection::Toggle:
1389 selection->add (touched);
1391 case Selection::Set:
1392 selection->set (touched);
1394 case Selection::Extend:
1395 /* meaningless, because we're selecting everything */
1397 case Selection::Add:
1398 selection->add (touched);
1402 commit_reversible_selection_op ();
1406 Editor::select_all_internal_edit (Selection::Operation)
1408 bool selected = false;
1410 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1411 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1413 mrv->select_all_notes ();
1418 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1420 mrv->select_all_notes ();
1428 Editor::select_all_objects (Selection::Operation op)
1430 list<Selectable *> touched;
1432 TrackViewList ts = track_views;
1434 if (internal_editing() && select_all_internal_edit(op)) {
1435 return; // Selected notes
1438 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1439 if ((*iter)->hidden()) {
1442 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1443 selection->add (*iter);
1447 begin_reversible_selection_op (X_("select all"));
1449 case Selection::Add:
1450 selection->add (touched);
1452 case Selection::Toggle:
1453 selection->add (touched);
1455 case Selection::Set:
1456 selection->set (touched);
1458 case Selection::Extend:
1459 /* meaningless, because we're selecting everything */
1462 commit_reversible_selection_op ();
1466 Editor::invert_selection_in_track ()
1468 list<Selectable *> touched;
1470 if (!clicked_routeview) {
1474 begin_reversible_selection_op (X_("Invert Selection in Track"));
1475 clicked_routeview->get_inverted_selectables (*selection, touched);
1476 selection->set (touched);
1477 commit_reversible_selection_op ();
1481 Editor::invert_selection ()
1483 list<Selectable *> touched;
1485 if (internal_editing()) {
1486 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1487 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1489 mrv->invert_selection ();
1495 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1496 if ((*iter)->hidden()) {
1499 (*iter)->get_inverted_selectables (*selection, touched);
1502 begin_reversible_selection_op (X_("Invert Selection"));
1503 selection->set (touched);
1504 commit_reversible_selection_op ();
1507 /** @param start Start time in session frames.
1508 * @param end End time in session frames.
1509 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1510 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1511 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1512 * within the region are already selected.
1515 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1517 list<Selectable*> found;
1519 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1521 if ((*iter)->hidden()) {
1525 (*iter)->get_selectables (start, end, top, bot, found);
1528 if (found.empty()) {
1529 selection->clear_objects();
1530 selection->clear_time ();
1534 if (preserve_if_selected && op != Selection::Toggle) {
1535 list<Selectable*>::iterator i = found.begin();
1536 while (i != found.end() && (*i)->get_selected()) {
1540 if (i == found.end()) {
1545 begin_reversible_selection_op (X_("select all within"));
1547 case Selection::Add:
1548 selection->add (found);
1550 case Selection::Toggle:
1551 selection->toggle (found);
1553 case Selection::Set:
1554 selection->set (found);
1556 case Selection::Extend:
1557 /* not defined yet */
1561 commit_reversible_selection_op ();
1565 Editor::set_selection_from_region ()
1567 if (selection->regions.empty()) {
1571 /* find all the tracks that have selected regions */
1573 set<TimeAxisView*> tracks;
1575 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1576 tracks.insert (&(*r)->get_time_axis_view());
1580 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1582 /* select range (this will clear the region selection) */
1584 selection->set (selection->regions.start(), selection->regions.end_frame());
1586 /* and select the tracks */
1588 selection->set (tvl);
1590 if (!Profile->get_sae()) {
1591 set_mouse_mode (Editing::MouseRange, false);
1596 Editor::set_selection_from_punch()
1600 if ((location = _session->locations()->auto_punch_location()) == 0) {
1604 set_selection_from_range (*location);
1608 Editor::set_selection_from_loop()
1612 if ((location = _session->locations()->auto_loop_location()) == 0) {
1615 set_selection_from_range (*location);
1619 Editor::set_selection_from_range (Location& loc)
1621 begin_reversible_selection_op (X_("set selection from range"));
1622 selection->set (loc.start(), loc.end());
1623 commit_reversible_selection_op ();
1625 if (!Profile->get_sae()) {
1626 set_mouse_mode (Editing::MouseRange, false);
1631 Editor::select_all_selectables_using_time_selection ()
1633 list<Selectable *> touched;
1635 if (selection->time.empty()) {
1639 framepos_t start = selection->time[clicked_selection].start;
1640 framepos_t end = selection->time[clicked_selection].end;
1642 if (end - start < 1) {
1648 if (selection->tracks.empty()) {
1651 ts = &selection->tracks;
1654 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1655 if ((*iter)->hidden()) {
1658 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1661 begin_reversible_selection_op (X_("select all from range"));
1662 selection->set (touched);
1663 commit_reversible_selection_op ();
1668 Editor::select_all_selectables_using_punch()
1670 Location* location = _session->locations()->auto_punch_location();
1671 list<Selectable *> touched;
1673 if (location == 0 || (location->end() - location->start() <= 1)) {
1680 if (selection->tracks.empty()) {
1683 ts = &selection->tracks;
1686 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1687 if ((*iter)->hidden()) {
1690 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1692 begin_reversible_selection_op (X_("select all from punch"));
1693 selection->set (touched);
1694 commit_reversible_selection_op ();
1699 Editor::select_all_selectables_using_loop()
1701 Location* location = _session->locations()->auto_loop_location();
1702 list<Selectable *> touched;
1704 if (location == 0 || (location->end() - location->start() <= 1)) {
1711 if (selection->tracks.empty()) {
1714 ts = &selection->tracks;
1717 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1718 if ((*iter)->hidden()) {
1721 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1723 begin_reversible_selection_op (X_("select all from loop"));
1724 selection->set (touched);
1725 commit_reversible_selection_op ();
1730 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1734 list<Selectable *> touched;
1737 start = cursor->current_frame();
1738 end = _session->current_end_frame();
1740 if (cursor->current_frame() > 0) {
1742 end = cursor->current_frame() - 1;
1748 if (internal_editing()) {
1749 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1750 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1752 mrv->select_range (start, end);
1759 begin_reversible_selection_op (X_("select all after cursor"));
1761 begin_reversible_selection_op (X_("select all before cursor"));
1766 if (selection->tracks.empty()) {
1769 ts = &selection->tracks;
1772 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1773 if ((*iter)->hidden()) {
1776 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1778 selection->set (touched);
1779 commit_reversible_selection_op ();
1783 Editor::select_all_selectables_using_edit (bool after)
1787 list<Selectable *> touched;
1790 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1791 end = _session->current_end_frame();
1793 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1801 if (internal_editing()) {
1802 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1803 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1804 mrv->select_range (start, end);
1810 begin_reversible_selection_op (X_("select all after edit"));
1812 begin_reversible_selection_op (X_("select all before edit"));
1817 if (selection->tracks.empty()) {
1820 ts = &selection->tracks;
1823 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1824 if ((*iter)->hidden()) {
1827 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1829 selection->set (touched);
1830 commit_reversible_selection_op ();
1834 Editor::select_all_selectables_between (bool within)
1838 list<Selectable *> touched;
1840 if (!get_edit_op_range (start, end)) {
1844 if (internal_editing()) {
1845 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1846 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1847 mrv->select_range (start, end);
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, within);
1867 begin_reversible_selection_op (X_("Select all Selectables Between"));
1868 selection->set (touched);
1869 commit_reversible_selection_op ();
1873 Editor::select_range_between ()
1878 if ( !selection->time.empty() ) {
1879 selection->clear_time ();
1882 if (!get_edit_op_range (start, end)) {
1886 begin_reversible_selection_op (X_("Select Range Between"));
1887 set_mouse_mode (MouseRange);
1888 selection->set (start, end);
1889 commit_reversible_selection_op ();
1893 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1898 /* if an explicit range exists, use it */
1900 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1901 /* we know that these are ordered */
1902 start = selection->time.start();
1903 end = selection->time.end_frame();
1911 // if (!mouse_frame (m, ignored)) {
1912 // /* mouse is not in a canvas, try playhead+selected marker.
1913 // this is probably most true when using menus.
1916 // if (selection->markers.empty()) {
1920 // start = selection->markers.front()->position();
1921 // end = _session->audible_frame();
1925 // switch (_edit_point) {
1926 // case EditAtPlayhead:
1927 // if (selection->markers.empty()) {
1928 // /* use mouse + playhead */
1930 // end = _session->audible_frame();
1932 // /* use playhead + selected marker */
1933 // start = _session->audible_frame();
1934 // end = selection->markers.front()->position();
1938 // case EditAtMouse:
1939 // /* use mouse + selected marker */
1940 // if (selection->markers.empty()) {
1942 // end = _session->audible_frame();
1944 // start = selection->markers.front()->position();
1949 // case EditAtSelectedMarker:
1950 // /* use mouse + selected marker */
1951 // if (selection->markers.empty()) {
1953 // MessageDialog win (_("No edit range defined"),
1958 // win.set_secondary_text (
1959 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1962 // win.set_default_response (RESPONSE_CLOSE);
1963 // win.set_position (Gtk::WIN_POS_MOUSE);
1968 // return false; // NO RANGE
1970 // start = selection->markers.front()->position();
1976 // if (start == end) {
1980 // if (start > end) {
1981 // swap (start, end);
1984 /* turn range into one delimited by start...end,
1994 Editor::deselect_all ()
1996 begin_reversible_selection_op (X_("Deselect All"));
1997 selection->clear ();
1998 commit_reversible_selection_op ();
2002 Editor::select_range (framepos_t s, framepos_t e)
2004 begin_reversible_selection_op (X_("Select Range"));
2005 selection->add (clicked_axisview);
2006 selection->time.clear ();
2007 long ret = selection->set (s, e);
2008 commit_reversible_selection_op ();