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 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
656 selection->set(all_equivalent_regions);
667 } else if (op == Selection::Extend) {
669 list<Selectable*> results;
670 framepos_t last_frame;
671 framepos_t first_frame;
672 bool same_track = false;
674 /* 1. find the last selected regionview in the track that was clicked in */
677 first_frame = max_framepos;
679 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
680 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
682 if ((*x)->region()->last_frame() > last_frame) {
683 last_frame = (*x)->region()->last_frame();
686 if ((*x)->region()->first_frame() < first_frame) {
687 first_frame = (*x)->region()->first_frame();
696 /* 2. figure out the boundaries for our search for new objects */
698 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
699 case Evoral::OverlapNone:
700 if (last_frame < clicked_regionview->region()->first_frame()) {
701 first_frame = last_frame;
702 last_frame = clicked_regionview->region()->last_frame();
704 last_frame = first_frame;
705 first_frame = clicked_regionview->region()->first_frame();
709 case Evoral::OverlapExternal:
710 if (last_frame < clicked_regionview->region()->first_frame()) {
711 first_frame = last_frame;
712 last_frame = clicked_regionview->region()->last_frame();
714 last_frame = first_frame;
715 first_frame = clicked_regionview->region()->first_frame();
719 case Evoral::OverlapInternal:
720 if (last_frame < clicked_regionview->region()->first_frame()) {
721 first_frame = last_frame;
722 last_frame = clicked_regionview->region()->last_frame();
724 last_frame = first_frame;
725 first_frame = clicked_regionview->region()->first_frame();
729 case Evoral::OverlapStart:
730 case Evoral::OverlapEnd:
731 /* nothing to do except add clicked region to selection, since it
732 overlaps with the existing selection in this track.
739 /* click in a track that has no regions selected, so extend vertically
740 to pick out all regions that are defined by the existing selection
745 first_frame = clicked_regionview->region()->position();
746 last_frame = clicked_regionview->region()->last_frame();
748 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
749 if ((*i)->region()->position() < first_frame) {
750 first_frame = (*i)->region()->position();
752 if ((*i)->region()->last_frame() + 1 > last_frame) {
753 last_frame = (*i)->region()->last_frame();
758 /* 2. find all the tracks we should select in */
760 set<RouteTimeAxisView*> relevant_tracks;
762 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
763 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
765 relevant_tracks.insert (r);
769 set<RouteTimeAxisView*> already_in_selection;
771 if (relevant_tracks.empty()) {
773 /* no tracks selected .. thus .. if the
774 regionview we're in isn't selected
775 (i.e. we're about to extend to it), then
776 find all tracks between the this one and
780 if (!selection->selected (clicked_regionview)) {
782 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
786 /* add this track to the ones we will search */
788 relevant_tracks.insert (rtv);
790 /* find the track closest to this one that
791 already a selected region.
794 RouteTimeAxisView* closest = 0;
795 int distance = INT_MAX;
796 int key = rtv->route()->order_key ();
798 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
800 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
802 if (artv && artv != rtv) {
804 pair<set<RouteTimeAxisView*>::iterator,bool> result;
806 result = already_in_selection.insert (artv);
809 /* newly added to already_in_selection */
811 int d = artv->route()->order_key ();
815 if (abs (d) < distance) {
825 /* now add all tracks between that one and this one */
827 int okey = closest->route()->order_key ();
833 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
834 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
835 if (artv && artv != rtv) {
837 int k = artv->route()->order_key ();
839 if (k >= okey && k <= key) {
841 /* in range but don't add it if
842 it already has tracks selected.
843 this avoids odd selection
844 behaviour that feels wrong.
847 if (find (already_in_selection.begin(),
848 already_in_selection.end(),
849 artv) == already_in_selection.end()) {
851 relevant_tracks.insert (artv);
861 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
862 one that was clicked.
865 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
866 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
869 /* 4. convert to a vector of regions */
871 vector<RegionView*> regions;
873 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
876 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
877 regions.push_back (arv);
881 if (!regions.empty()) {
882 selection->add (regions);
893 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
895 vector<RegionView*> all_equivalent_regions;
897 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
899 if (all_equivalent_regions.empty()) {
903 begin_reversible_selection_op (X_("set selected regions"));
906 case Selection::Toggle:
907 /* XXX this is not correct */
908 selection->toggle (all_equivalent_regions);
911 selection->set (all_equivalent_regions);
913 case Selection::Extend:
914 selection->add (all_equivalent_regions);
917 selection->add (all_equivalent_regions);
921 commit_reversible_selection_op () ;
925 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
928 boost::shared_ptr<Region> r (weak_r.lock());
934 if ((rv = sv->find_view (r)) == 0) {
938 /* don't reset the selection if its something other than
939 a single other region.
942 if (selection->regions.size() > 1) {
946 begin_reversible_selection_op (X_("set selected regions"));
950 commit_reversible_selection_op () ;
956 Editor::track_selection_changed ()
958 switch (selection->tracks.size()) {
962 set_selected_mixer_strip (*(selection->tracks.front()));
966 RouteNotificationListPtr routes (new RouteNotificationList);
968 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
970 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
972 (*i)->set_selected (yn);
974 TimeAxisView::Children c = (*i)->get_child_list ();
975 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
976 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
980 (*i)->reshow_selection (selection->time);
982 (*i)->hide_selection ();
987 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
989 routes->push_back (rtav->route());
994 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
996 /* notify control protocols */
998 ControlProtocol::TrackSelectionChanged (routes);
1002 Editor::time_selection_changed ()
1004 if (Profile->get_sae()) {
1008 /* XXX this is superficially inefficient. Hide the selection in all
1009 * tracks, then show it in all selected tracks.
1011 * However, if you investigate what this actually does, it isn't
1012 * anywhere nearly as bad as it may appear. Remember: nothing is
1013 * redrawn or even recomputed during these two loops - that only
1014 * happens when we next render ...
1017 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1018 (*i)->hide_selection ();
1021 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1022 (*i)->show_selection (selection->time);
1025 if (selection->time.empty()) {
1026 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1028 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1032 /** Set all region actions to have a given sensitivity */
1034 Editor::sensitize_all_region_actions (bool s)
1036 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1038 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1039 (*i)->set_sensitive (s);
1042 _all_region_actions_sensitized = s;
1045 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1046 * This method should be called just before displaying a Region menu. When a Region menu is not
1047 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1048 * on entered_regionviews work without having to check sensitivity every time the selection or
1049 * entered_regionview changes.
1051 * This method also sets up toggle action state as appropriate.
1054 Editor::sensitize_the_right_region_actions ()
1057 RegionSelection rs = get_regions_from_selection_and_entered ();
1058 sensitize_all_region_actions (!rs.empty ());
1060 _ignore_region_action = true;
1062 /* Look through the regions that are selected and make notes about what we have got */
1064 bool have_audio = false;
1065 bool have_multichannel_audio = false;
1066 bool have_midi = false;
1067 bool have_locked = false;
1068 bool have_unlocked = false;
1069 bool have_video_locked = false;
1070 bool have_video_unlocked = false;
1071 bool have_position_lock_style_audio = false;
1072 bool have_position_lock_style_music = false;
1073 bool have_muted = false;
1074 bool have_unmuted = false;
1075 bool have_opaque = false;
1076 bool have_non_opaque = false;
1077 bool have_not_at_natural_position = false;
1078 bool have_envelope_active = false;
1079 bool have_envelope_inactive = false;
1080 bool have_non_unity_scale_amplitude = false;
1081 bool have_compound_regions = false;
1082 bool have_inactive_fade_in = false;
1083 bool have_inactive_fade_out = false;
1084 bool have_active_fade_in = false;
1085 bool have_active_fade_out = false;
1087 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1089 boost::shared_ptr<Region> r = (*i)->region ();
1090 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1094 if (ar->n_channels() > 1) {
1095 have_multichannel_audio = true;
1099 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1103 if (r->is_compound()) {
1104 have_compound_regions = true;
1110 have_unlocked = true;
1113 if (r->video_locked()) {
1114 have_video_locked = true;
1116 have_video_unlocked = true;
1119 if (r->position_lock_style() == MusicTime) {
1120 have_position_lock_style_music = true;
1122 have_position_lock_style_audio = true;
1128 have_unmuted = true;
1134 have_non_opaque = true;
1137 if (!r->at_natural_position()) {
1138 have_not_at_natural_position = true;
1142 if (ar->envelope_active()) {
1143 have_envelope_active = true;
1145 have_envelope_inactive = true;
1148 if (ar->scale_amplitude() != 1) {
1149 have_non_unity_scale_amplitude = true;
1152 if (ar->fade_in_active ()) {
1153 have_active_fade_in = true;
1155 have_inactive_fade_in = true;
1158 if (ar->fade_out_active ()) {
1159 have_active_fade_out = true;
1161 have_inactive_fade_out = true;
1166 if (rs.size() > 1) {
1167 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1168 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1169 _region_actions->get_action("rename-region")->set_sensitive (false);
1171 /* XXX need to check whether there is than 1 per
1172 playlist, because otherwise this makes no sense.
1174 _region_actions->get_action("combine-regions")->set_sensitive (true);
1176 _region_actions->get_action("combine-regions")->set_sensitive (false);
1178 } else if (rs.size() == 1) {
1179 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1180 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1181 _region_actions->get_action("combine-regions")->set_sensitive (false);
1184 if (!have_multichannel_audio) {
1185 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1189 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1190 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1191 _region_actions->get_action("quantize-region")->set_sensitive (false);
1192 _region_actions->get_action("legatize-region")->set_sensitive (false);
1193 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1194 _region_actions->get_action("fork-region")->set_sensitive (false);
1195 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1196 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1197 _region_actions->get_action("transpose-region")->set_sensitive (false);
1199 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1200 /* others were already marked sensitive */
1203 if (_edit_point == EditAtMouse) {
1204 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1205 _region_actions->get_action("trim-front")->set_sensitive (false);
1206 _region_actions->get_action("trim-back")->set_sensitive (false);
1207 _region_actions->get_action("split-region")->set_sensitive (false);
1208 _region_actions->get_action("place-transient")->set_sensitive (false);
1211 if (have_compound_regions) {
1212 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1214 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1219 if (have_envelope_active && !have_envelope_inactive) {
1220 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1221 } else if (have_envelope_active && have_envelope_inactive) {
1222 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1227 _region_actions->get_action("analyze-region")->set_sensitive (false);
1228 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1229 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1230 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1234 if (!have_non_unity_scale_amplitude || !have_audio) {
1235 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1238 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1239 a->set_active (have_locked && !have_unlocked);
1240 if (have_locked && have_unlocked) {
1241 // a->set_inconsistent ();
1244 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1245 a->set_active (have_video_locked && !have_video_unlocked);
1246 if (have_video_locked && have_video_unlocked) {
1247 // a->set_inconsistent ();
1250 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1251 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1253 if (have_position_lock_style_music && have_position_lock_style_audio) {
1254 // a->set_inconsistent ();
1257 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1258 a->set_active (have_muted && !have_unmuted);
1259 if (have_muted && have_unmuted) {
1260 // a->set_inconsistent ();
1263 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1264 a->set_active (have_opaque && !have_non_opaque);
1265 if (have_opaque && have_non_opaque) {
1266 // a->set_inconsistent ();
1269 if (!have_not_at_natural_position) {
1270 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1273 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1274 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1275 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1277 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1280 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1281 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1282 if (have_active_fade_in && have_inactive_fade_in) {
1283 // a->set_inconsistent ();
1286 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1287 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1289 if (have_active_fade_out && have_inactive_fade_out) {
1290 // a->set_inconsistent ();
1293 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1294 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1296 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1297 a->set_active (have_active_fade && !have_inactive_fade);
1299 if (have_active_fade && have_inactive_fade) {
1300 // a->set_inconsistent ();
1303 _ignore_region_action = false;
1305 _all_region_actions_sensitized = false;
1310 Editor::region_selection_changed ()
1312 _regions->block_change_connection (true);
1313 editor_regions_selection_changed_connection.block(true);
1315 if (_region_selection_change_updates_region_list) {
1316 _regions->unselect_all ();
1319 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1320 (*i)->set_selected_regionviews (selection->regions);
1323 if (_region_selection_change_updates_region_list) {
1324 _regions->set_selected (selection->regions);
1327 _regions->block_change_connection (false);
1328 editor_regions_selection_changed_connection.block(false);
1330 if (!_all_region_actions_sensitized) {
1331 /* This selection change might have changed what region actions
1332 are allowed, so sensitize them all in case a key is pressed.
1334 sensitize_all_region_actions (true);
1337 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1338 maybe_locate_with_edit_preroll (selection->regions.start());
1343 Editor::point_selection_changed ()
1345 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1346 (*i)->set_selected_points (selection->points);
1351 Editor::select_all_in_track (Selection::Operation op)
1353 list<Selectable *> touched;
1355 if (!clicked_routeview) {
1359 begin_reversible_selection_op (X_("Select All in Track"));
1361 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1364 case Selection::Toggle:
1365 selection->add (touched);
1367 case Selection::Set:
1368 selection->set (touched);
1370 case Selection::Extend:
1371 /* meaningless, because we're selecting everything */
1373 case Selection::Add:
1374 selection->add (touched);
1378 commit_reversible_selection_op ();
1382 Editor::select_all_internal_edit (Selection::Operation)
1384 bool selected = false;
1386 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1387 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1389 mrv->select_all_notes ();
1394 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1396 mrv->select_all_notes ();
1404 Editor::select_all_objects (Selection::Operation op)
1406 list<Selectable *> touched;
1408 TrackViewList ts = track_views;
1410 if (internal_editing() && select_all_internal_edit(op)) {
1411 return; // Selected notes
1414 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1415 if ((*iter)->hidden()) {
1418 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1419 selection->add (*iter);
1423 begin_reversible_selection_op (X_("select all"));
1425 case Selection::Add:
1426 selection->add (touched);
1428 case Selection::Toggle:
1429 selection->add (touched);
1431 case Selection::Set:
1432 selection->set (touched);
1434 case Selection::Extend:
1435 /* meaningless, because we're selecting everything */
1438 commit_reversible_selection_op ();
1442 Editor::invert_selection_in_track ()
1444 list<Selectable *> touched;
1446 if (!clicked_routeview) {
1450 begin_reversible_selection_op (X_("Invert Selection in Track"));
1451 clicked_routeview->get_inverted_selectables (*selection, touched);
1452 selection->set (touched);
1453 commit_reversible_selection_op ();
1457 Editor::invert_selection ()
1459 list<Selectable *> touched;
1461 if (internal_editing()) {
1462 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1463 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1465 mrv->invert_selection ();
1471 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1472 if ((*iter)->hidden()) {
1475 (*iter)->get_inverted_selectables (*selection, touched);
1478 begin_reversible_selection_op (X_("Invert Selection"));
1479 selection->set (touched);
1480 commit_reversible_selection_op ();
1483 /** @param start Start time in session frames.
1484 * @param end End time in session frames.
1485 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1486 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1487 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1488 * within the region are already selected.
1491 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1493 list<Selectable*> found;
1495 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1497 if ((*iter)->hidden()) {
1501 (*iter)->get_selectables (start, end, top, bot, found);
1504 if (found.empty()) {
1505 selection->clear_objects();
1506 selection->clear_time ();
1510 if (preserve_if_selected && op != Selection::Toggle) {
1511 list<Selectable*>::iterator i = found.begin();
1512 while (i != found.end() && (*i)->get_selected()) {
1516 if (i == found.end()) {
1521 begin_reversible_selection_op (X_("select all within"));
1523 case Selection::Add:
1524 selection->add (found);
1526 case Selection::Toggle:
1527 selection->toggle (found);
1529 case Selection::Set:
1530 selection->set (found);
1532 case Selection::Extend:
1533 /* not defined yet */
1537 commit_reversible_selection_op ();
1541 Editor::set_selection_from_region ()
1543 if (selection->regions.empty()) {
1547 selection->set (selection->regions.start(), selection->regions.end_frame());
1549 //we must now select tracks, because otherwise set_selection_from_region would appear to do nothing
1550 //perhaps too drastic; perhaps the user really only wants the region's track selected
1551 //but I can't think of any use-case for that (why wouldn't you just select the region?)
1552 select_all_tracks();
1554 if (!Profile->get_sae()) {
1555 set_mouse_mode (Editing::MouseRange, false);
1560 Editor::set_selection_from_punch()
1564 if ((location = _session->locations()->auto_punch_location()) == 0) {
1568 set_selection_from_range (*location);
1572 Editor::set_selection_from_loop()
1576 if ((location = _session->locations()->auto_loop_location()) == 0) {
1579 set_selection_from_range (*location);
1583 Editor::set_selection_from_range (Location& loc)
1585 begin_reversible_selection_op (X_("set selection from range"));
1586 selection->set (loc.start(), loc.end());
1587 commit_reversible_selection_op ();
1589 if (!Profile->get_sae()) {
1590 set_mouse_mode (Editing::MouseRange, false);
1595 Editor::select_all_selectables_using_time_selection ()
1597 list<Selectable *> touched;
1599 if (selection->time.empty()) {
1603 framepos_t start = selection->time[clicked_selection].start;
1604 framepos_t end = selection->time[clicked_selection].end;
1606 if (end - start < 1) {
1612 if (selection->tracks.empty()) {
1615 ts = &selection->tracks;
1618 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1619 if ((*iter)->hidden()) {
1622 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1625 begin_reversible_selection_op (X_("select all from range"));
1626 selection->set (touched);
1627 commit_reversible_selection_op ();
1632 Editor::select_all_selectables_using_punch()
1634 Location* location = _session->locations()->auto_punch_location();
1635 list<Selectable *> touched;
1637 if (location == 0 || (location->end() - location->start() <= 1)) {
1644 if (selection->tracks.empty()) {
1647 ts = &selection->tracks;
1650 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1651 if ((*iter)->hidden()) {
1654 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1656 begin_reversible_selection_op (X_("select all from punch"));
1657 selection->set (touched);
1658 commit_reversible_selection_op ();
1663 Editor::select_all_selectables_using_loop()
1665 Location* location = _session->locations()->auto_loop_location();
1666 list<Selectable *> touched;
1668 if (location == 0 || (location->end() - location->start() <= 1)) {
1675 if (selection->tracks.empty()) {
1678 ts = &selection->tracks;
1681 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1682 if ((*iter)->hidden()) {
1685 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1687 begin_reversible_selection_op (X_("select all from loop"));
1688 selection->set (touched);
1689 commit_reversible_selection_op ();
1694 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1698 list<Selectable *> touched;
1701 start = cursor->current_frame();
1702 end = _session->current_end_frame();
1704 if (cursor->current_frame() > 0) {
1706 end = cursor->current_frame() - 1;
1712 if (internal_editing()) {
1713 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1714 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1716 mrv->select_range (start, end);
1723 begin_reversible_selection_op (X_("select all after cursor"));
1725 begin_reversible_selection_op (X_("select all before cursor"));
1730 if (selection->tracks.empty()) {
1733 ts = &selection->tracks;
1736 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1737 if ((*iter)->hidden()) {
1740 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1742 selection->set (touched);
1743 commit_reversible_selection_op ();
1747 Editor::select_all_selectables_using_edit (bool after)
1751 list<Selectable *> touched;
1754 start = get_preferred_edit_position(false, true);
1755 end = _session->current_end_frame();
1757 if ((end = get_preferred_edit_position(false, true)) > 1) {
1765 if (internal_editing()) {
1766 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1767 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1768 mrv->select_range (start, end);
1774 begin_reversible_selection_op (X_("select all after edit"));
1776 begin_reversible_selection_op (X_("select all before edit"));
1781 if (selection->tracks.empty()) {
1784 ts = &selection->tracks;
1787 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1788 if ((*iter)->hidden()) {
1791 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1793 selection->set (touched);
1794 commit_reversible_selection_op ();
1798 Editor::select_all_selectables_between (bool within)
1802 list<Selectable *> touched;
1804 if (!get_edit_op_range (start, end)) {
1808 if (internal_editing()) {
1809 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1810 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1811 mrv->select_range (start, end);
1818 if (selection->tracks.empty()) {
1821 ts = &selection->tracks;
1824 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1825 if ((*iter)->hidden()) {
1828 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1831 begin_reversible_selection_op (X_("Select all Selectables Between"));
1832 selection->set (touched);
1833 commit_reversible_selection_op ();
1837 Editor::select_range_between ()
1842 if ( !selection->time.empty() ) {
1843 selection->clear_time ();
1846 if (!get_edit_op_range (start, end)) {
1850 begin_reversible_selection_op (X_("Select Range Between"));
1851 set_mouse_mode (MouseRange);
1852 selection->set (start, end);
1853 commit_reversible_selection_op ();
1857 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1862 /* if an explicit range exists, use it */
1864 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1865 /* we know that these are ordered */
1866 start = selection->time.start();
1867 end = selection->time.end_frame();
1875 // if (!mouse_frame (m, ignored)) {
1876 // /* mouse is not in a canvas, try playhead+selected marker.
1877 // this is probably most true when using menus.
1880 // if (selection->markers.empty()) {
1884 // start = selection->markers.front()->position();
1885 // end = _session->audible_frame();
1889 // switch (_edit_point) {
1890 // case EditAtPlayhead:
1891 // if (selection->markers.empty()) {
1892 // /* use mouse + playhead */
1894 // end = _session->audible_frame();
1896 // /* use playhead + selected marker */
1897 // start = _session->audible_frame();
1898 // end = selection->markers.front()->position();
1902 // case EditAtMouse:
1903 // /* use mouse + selected marker */
1904 // if (selection->markers.empty()) {
1906 // end = _session->audible_frame();
1908 // start = selection->markers.front()->position();
1913 // case EditAtSelectedMarker:
1914 // /* use mouse + selected marker */
1915 // if (selection->markers.empty()) {
1917 // MessageDialog win (_("No edit range defined"),
1922 // win.set_secondary_text (
1923 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1926 // win.set_default_response (RESPONSE_CLOSE);
1927 // win.set_position (Gtk::WIN_POS_MOUSE);
1932 // return false; // NO RANGE
1934 // start = selection->markers.front()->position();
1940 // if (start == end) {
1944 // if (start > end) {
1945 // swap (start, end);
1948 /* turn range into one delimited by start...end,
1958 Editor::deselect_all ()
1960 begin_reversible_selection_op (X_("Deselect All"));
1961 selection->clear ();
1962 commit_reversible_selection_op ();
1966 Editor::select_range (framepos_t s, framepos_t e)
1968 begin_reversible_selection_op (X_("Select Range"));
1969 selection->add (clicked_axisview);
1970 selection->time.clear ();
1971 long ret = selection->set (s, e);
1972 commit_reversible_selection_op ();