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) {
334 selection->set (clicked_control_point);
340 selection->add (clicked_control_point);
344 case Selection::Toggle:
346 /* This is a bit of a hack; if we Primary-Click-Drag a control
347 point (for push drag) we want the point we clicked on to be
348 selected, otherwise we end up confusingly dragging an
349 unselected point. So here we ensure that the point is selected
350 after the press, and if we subsequently get a release (meaning no
351 drag occurred) we set things up so that the toggle has happened.
353 if (press && !selection->selected (clicked_control_point)) {
354 /* This is the button press, and the control point is not selected; make it so,
355 in case this press leads to a drag. Also note that having done this, we don't
356 need to toggle again on release.
358 selection->toggle (clicked_control_point);
359 _control_point_toggled_on_press = true;
361 } else if (!press && !_control_point_toggled_on_press) {
362 /* This is the release, and the point wasn't toggled on the press, so do it now */
363 selection->toggle (clicked_control_point);
367 _control_point_toggled_on_press = false;
370 case Selection::Extend:
379 Editor::get_onscreen_tracks (TrackViewList& tvl)
381 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
382 if ((*i)->y_position() < _visible_canvas_height) {
388 /** Call a slot for a given `basis' track and also for any track that is in the same
389 * active route group with a particular set of properties.
391 * @param sl Slot to call.
392 * @param basis Basis track.
393 * @param prop Properties that active edit groups must share to be included in the map.
397 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
399 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
401 if (route_basis == 0) {
405 set<RouteTimeAxisView*> tracks;
406 tracks.insert (route_basis);
408 RouteGroup* group = route_basis->route()->route_group();
410 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
412 /* the basis is a member of an active route group, with the appropriate
413 properties; find other members */
415 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
416 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
417 if (v && v->route()->route_group() == group) {
424 uint32_t const sz = tracks.size ();
426 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
431 /** Call a slot for a given `basis' track and also for any track that is in the same
432 * active route group with a particular set of properties.
434 * @param sl Slot to call.
435 * @param basis Basis track.
436 * @param prop Properties that active edit groups must share to be included in the map.
440 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
442 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
443 set<boost::shared_ptr<Playlist> > playlists;
445 if (route_basis == 0) {
449 set<RouteTimeAxisView*> tracks;
450 tracks.insert (route_basis);
452 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
454 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
456 /* the basis is a member of an active route group, with the appropriate
457 properties; find other members */
459 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
460 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
462 if (v && v->route()->route_group() == group) {
464 boost::shared_ptr<Track> t = v->track();
466 if (playlists.insert (t->playlist()).second) {
467 /* haven't seen this playlist yet */
471 /* not actually a "Track", but a timeaxis view that
472 we should mapover anyway.
481 uint32_t const sz = tracks.size ();
483 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
489 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
491 boost::shared_ptr<Playlist> pl;
492 vector<boost::shared_ptr<Region> > results;
494 boost::shared_ptr<Track> tr;
496 if ((tr = tv.track()) == 0) {
501 if (&tv == &basis->get_time_axis_view()) {
502 /* looking in same track as the original */
506 if ((pl = tr->playlist()) != 0) {
507 pl->get_equivalent_regions (basis->region(), results);
510 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
511 if ((marv = tv.view()->find_view (*ir)) != 0) {
512 all_equivs->push_back (marv);
518 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
520 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);
522 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
524 equivalent_regions.push_back (basis);
528 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
530 RegionSelection equivalent;
532 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
534 vector<RegionView*> eq;
536 mapover_tracks_with_unique_playlists (
537 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
538 &(*i)->get_time_axis_view(), prop);
540 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
551 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
553 int region_count = 0;
555 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
557 RouteTimeAxisView* tatv;
559 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
561 boost::shared_ptr<Playlist> pl;
562 vector<boost::shared_ptr<Region> > results;
564 boost::shared_ptr<Track> tr;
566 if ((tr = tatv->track()) == 0) {
571 if ((pl = (tr->playlist())) != 0) {
572 pl->get_region_list_equivalent_regions (region, results);
575 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
576 if ((marv = tatv->view()->find_view (*ir)) != 0) {
589 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
591 vector<RegionView*> all_equivalent_regions;
594 if (!clicked_regionview || !clicked_routeview) {
599 button_release_can_deselect = false;
602 if (op == Selection::Toggle || op == Selection::Set) {
605 case Selection::Toggle:
606 if (selection->selected (clicked_regionview)) {
609 /* whatever was clicked was selected already; do nothing here but allow
610 the button release to deselect it
613 button_release_can_deselect = true;
616 if (button_release_can_deselect) {
618 /* just remove this one region, but only on a permitted button release */
620 selection->remove (clicked_regionview);
623 /* no more deselect action on button release till a new press
624 finds an already selected object.
627 button_release_can_deselect = false;
635 if (selection->selected (clicked_routeview)) {
636 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
638 all_equivalent_regions.push_back (clicked_regionview);
641 /* add all the equivalent regions, but only on button press */
643 if (!all_equivalent_regions.empty()) {
647 selection->add (all_equivalent_regions);
653 if (!selection->selected (clicked_regionview)) {
654 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
655 selection->set (all_equivalent_regions);
658 /* clicked on an already selected region */
662 if (selection->regions.size() > 1) {
663 /* collapse region selection down to just this one region (and its equivalents) */
664 get_equivalent_regions(clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
665 selection->set(all_equivalent_regions);
677 } else if (op == Selection::Extend) {
679 list<Selectable*> results;
680 framepos_t last_frame;
681 framepos_t first_frame;
682 bool same_track = false;
684 /* 1. find the last selected regionview in the track that was clicked in */
687 first_frame = max_framepos;
689 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
690 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
692 if ((*x)->region()->last_frame() > last_frame) {
693 last_frame = (*x)->region()->last_frame();
696 if ((*x)->region()->first_frame() < first_frame) {
697 first_frame = (*x)->region()->first_frame();
706 /* 2. figure out the boundaries for our search for new objects */
708 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
709 case Evoral::OverlapNone:
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::OverlapExternal:
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::OverlapInternal:
730 if (last_frame < clicked_regionview->region()->first_frame()) {
731 first_frame = last_frame;
732 last_frame = clicked_regionview->region()->last_frame();
734 last_frame = first_frame;
735 first_frame = clicked_regionview->region()->first_frame();
739 case Evoral::OverlapStart:
740 case Evoral::OverlapEnd:
741 /* nothing to do except add clicked region to selection, since it
742 overlaps with the existing selection in this track.
749 /* click in a track that has no regions selected, so extend vertically
750 to pick out all regions that are defined by the existing selection
755 first_frame = clicked_regionview->region()->position();
756 last_frame = clicked_regionview->region()->last_frame();
758 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
759 if ((*i)->region()->position() < first_frame) {
760 first_frame = (*i)->region()->position();
762 if ((*i)->region()->last_frame() + 1 > last_frame) {
763 last_frame = (*i)->region()->last_frame();
768 /* 2. find all the tracks we should select in */
770 set<RouteTimeAxisView*> relevant_tracks;
772 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
773 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
775 relevant_tracks.insert (r);
779 set<RouteTimeAxisView*> already_in_selection;
781 if (relevant_tracks.empty()) {
783 /* no tracks selected .. thus .. if the
784 regionview we're in isn't selected
785 (i.e. we're about to extend to it), then
786 find all tracks between the this one and
790 if (!selection->selected (clicked_regionview)) {
792 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
796 /* add this track to the ones we will search */
798 relevant_tracks.insert (rtv);
800 /* find the track closest to this one that
801 already a selected region.
804 RouteTimeAxisView* closest = 0;
805 int distance = INT_MAX;
806 int key = rtv->route()->order_key ();
808 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
810 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
812 if (artv && artv != rtv) {
814 pair<set<RouteTimeAxisView*>::iterator,bool> result;
816 result = already_in_selection.insert (artv);
819 /* newly added to already_in_selection */
821 int d = artv->route()->order_key ();
825 if (abs (d) < distance) {
835 /* now add all tracks between that one and this one */
837 int okey = closest->route()->order_key ();
843 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
844 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
845 if (artv && artv != rtv) {
847 int k = artv->route()->order_key ();
849 if (k >= okey && k <= key) {
851 /* in range but don't add it if
852 it already has tracks selected.
853 this avoids odd selection
854 behaviour that feels wrong.
857 if (find (already_in_selection.begin(),
858 already_in_selection.end(),
859 artv) == already_in_selection.end()) {
861 relevant_tracks.insert (artv);
871 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
872 one that was clicked.
875 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
876 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
879 /* 4. convert to a vector of regions */
881 vector<RegionView*> regions;
883 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
886 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
887 regions.push_back (arv);
891 if (!regions.empty()) {
892 selection->add (regions);
903 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
905 vector<RegionView*> all_equivalent_regions;
907 get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
909 if (all_equivalent_regions.empty()) {
913 begin_reversible_selection_op (X_("set selected regions"));
916 case Selection::Toggle:
917 /* XXX this is not correct */
918 selection->toggle (all_equivalent_regions);
921 selection->set (all_equivalent_regions);
923 case Selection::Extend:
924 selection->add (all_equivalent_regions);
927 selection->add (all_equivalent_regions);
931 commit_reversible_selection_op () ;
935 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
938 boost::shared_ptr<Region> r (weak_r.lock());
944 if ((rv = sv->find_view (r)) == 0) {
948 /* don't reset the selection if its something other than
949 a single other region.
952 if (selection->regions.size() > 1) {
956 begin_reversible_selection_op (X_("set selected regions"));
960 commit_reversible_selection_op () ;
966 Editor::track_selection_changed ()
968 switch (selection->tracks.size()) {
972 set_selected_mixer_strip (*(selection->tracks.front()));
976 RouteNotificationListPtr routes (new RouteNotificationList);
978 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
980 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
982 (*i)->set_selected (yn);
984 TimeAxisView::Children c = (*i)->get_child_list ();
985 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
986 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
990 (*i)->reshow_selection (selection->time);
992 (*i)->hide_selection ();
997 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
999 routes->push_back (rtav->route());
1004 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
1006 /* notify control protocols */
1008 ControlProtocol::TrackSelectionChanged (routes);
1012 Editor::time_selection_changed ()
1014 if (Profile->get_sae()) {
1018 /* XXX this is superficially inefficient. Hide the selection in all
1019 * tracks, then show it in all selected tracks.
1021 * However, if you investigate what this actually does, it isn't
1022 * anywhere nearly as bad as it may appear. Remember: nothing is
1023 * redrawn or even recomputed during these two loops - that only
1024 * happens when we next render ...
1027 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1028 (*i)->hide_selection ();
1031 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1032 (*i)->show_selection (selection->time);
1035 if (selection->time.empty()) {
1036 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1038 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1041 /* propagate into backend, but only when there is no drag or we are at
1042 * the end of a drag, otherwise this is too expensive (could case a
1043 * locate per mouse motion event.
1046 if (_session && !_drags->active()) {
1047 if (selection->time.length() != 0) {
1048 _session->set_range_selection (selection->time.start(), selection->time.end_frame());
1050 _session->clear_range_selection ();
1055 /** Set all region actions to have a given sensitivity */
1057 Editor::sensitize_all_region_actions (bool s)
1059 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1061 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1062 (*i)->set_sensitive (s);
1065 _all_region_actions_sensitized = s;
1068 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1069 * This method should be called just before displaying a Region menu. When a Region menu is not
1070 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1071 * on entered_regionviews work without having to check sensitivity every time the selection or
1072 * entered_regionview changes.
1074 * This method also sets up toggle action state as appropriate.
1077 Editor::sensitize_the_right_region_actions ()
1080 RegionSelection rs = get_regions_from_selection_and_entered ();
1081 sensitize_all_region_actions (!rs.empty ());
1083 _ignore_region_action = true;
1085 /* Look through the regions that are selected and make notes about what we have got */
1087 bool have_audio = false;
1088 bool have_multichannel_audio = false;
1089 bool have_midi = false;
1090 bool have_locked = false;
1091 bool have_unlocked = false;
1092 bool have_video_locked = false;
1093 bool have_video_unlocked = false;
1094 bool have_position_lock_style_audio = false;
1095 bool have_position_lock_style_music = false;
1096 bool have_muted = false;
1097 bool have_unmuted = false;
1098 bool have_opaque = false;
1099 bool have_non_opaque = false;
1100 bool have_not_at_natural_position = false;
1101 bool have_envelope_active = false;
1102 bool have_envelope_inactive = false;
1103 bool have_non_unity_scale_amplitude = false;
1104 bool have_compound_regions = false;
1105 bool have_inactive_fade_in = false;
1106 bool have_inactive_fade_out = false;
1107 bool have_active_fade_in = false;
1108 bool have_active_fade_out = false;
1110 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1112 boost::shared_ptr<Region> r = (*i)->region ();
1113 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1117 if (ar->n_channels() > 1) {
1118 have_multichannel_audio = true;
1122 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1126 if (r->is_compound()) {
1127 have_compound_regions = true;
1133 have_unlocked = true;
1136 if (r->video_locked()) {
1137 have_video_locked = true;
1139 have_video_unlocked = true;
1142 if (r->position_lock_style() == MusicTime) {
1143 have_position_lock_style_music = true;
1145 have_position_lock_style_audio = true;
1151 have_unmuted = true;
1157 have_non_opaque = true;
1160 if (!r->at_natural_position()) {
1161 have_not_at_natural_position = true;
1165 if (ar->envelope_active()) {
1166 have_envelope_active = true;
1168 have_envelope_inactive = true;
1171 if (ar->scale_amplitude() != 1) {
1172 have_non_unity_scale_amplitude = true;
1175 if (ar->fade_in_active ()) {
1176 have_active_fade_in = true;
1178 have_inactive_fade_in = true;
1181 if (ar->fade_out_active ()) {
1182 have_active_fade_out = true;
1184 have_inactive_fade_out = true;
1189 if (rs.size() > 1) {
1190 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1191 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1192 _region_actions->get_action("rename-region")->set_sensitive (false);
1194 /* XXX need to check whether there is than 1 per
1195 playlist, because otherwise this makes no sense.
1197 _region_actions->get_action("combine-regions")->set_sensitive (true);
1199 _region_actions->get_action("combine-regions")->set_sensitive (false);
1201 } else if (rs.size() == 1) {
1202 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1203 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1204 _region_actions->get_action("combine-regions")->set_sensitive (false);
1207 if (!have_multichannel_audio) {
1208 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1212 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1213 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1214 _region_actions->get_action("quantize-region")->set_sensitive (false);
1215 _region_actions->get_action("legatize-region")->set_sensitive (false);
1216 _region_actions->get_action("remove-overlap")->set_sensitive (false);
1217 _region_actions->get_action("fork-region")->set_sensitive (false);
1218 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1219 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1220 _region_actions->get_action("transpose-region")->set_sensitive (false);
1222 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1223 /* others were already marked sensitive */
1226 if (_edit_point == EditAtMouse) {
1227 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1228 _region_actions->get_action("trim-front")->set_sensitive (false);
1229 _region_actions->get_action("trim-back")->set_sensitive (false);
1230 _region_actions->get_action("split-region")->set_sensitive (false);
1231 _region_actions->get_action("place-transient")->set_sensitive (false);
1234 if (have_compound_regions) {
1235 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1237 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1242 if (have_envelope_active && !have_envelope_inactive) {
1243 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1244 } else if (have_envelope_active && have_envelope_inactive) {
1245 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1250 _region_actions->get_action("analyze-region")->set_sensitive (false);
1251 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1252 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1253 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1257 if (!have_non_unity_scale_amplitude || !have_audio) {
1258 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1261 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1262 a->set_active (have_locked && !have_unlocked);
1263 if (have_locked && have_unlocked) {
1264 // a->set_inconsistent ();
1267 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1268 a->set_active (have_video_locked && !have_video_unlocked);
1269 if (have_video_locked && have_video_unlocked) {
1270 // a->set_inconsistent ();
1273 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1274 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1276 if (have_position_lock_style_music && have_position_lock_style_audio) {
1277 // a->set_inconsistent ();
1280 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1281 a->set_active (have_muted && !have_unmuted);
1282 if (have_muted && have_unmuted) {
1283 // a->set_inconsistent ();
1286 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1287 a->set_active (have_opaque && !have_non_opaque);
1288 if (have_opaque && have_non_opaque) {
1289 // a->set_inconsistent ();
1292 if (!have_not_at_natural_position) {
1293 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1296 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1297 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1298 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1300 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1303 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1304 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1305 if (have_active_fade_in && have_inactive_fade_in) {
1306 // a->set_inconsistent ();
1309 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1310 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1312 if (have_active_fade_out && have_inactive_fade_out) {
1313 // a->set_inconsistent ();
1316 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1317 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1319 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1320 a->set_active (have_active_fade && !have_inactive_fade);
1322 if (have_active_fade && have_inactive_fade) {
1323 // a->set_inconsistent ();
1326 _ignore_region_action = false;
1328 _all_region_actions_sensitized = false;
1333 Editor::region_selection_changed ()
1335 _regions->block_change_connection (true);
1336 editor_regions_selection_changed_connection.block(true);
1338 if (_region_selection_change_updates_region_list) {
1339 _regions->unselect_all ();
1342 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1343 (*i)->set_selected_regionviews (selection->regions);
1346 if (_region_selection_change_updates_region_list) {
1347 _regions->set_selected (selection->regions);
1350 _regions->block_change_connection (false);
1351 editor_regions_selection_changed_connection.block(false);
1353 if (!_all_region_actions_sensitized) {
1354 /* This selection change might have changed what region actions
1355 are allowed, so sensitize them all in case a key is pressed.
1357 sensitize_all_region_actions (true);
1360 if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1361 maybe_locate_with_edit_preroll (selection->regions.start());
1364 /* propagate into backend */
1367 if (!selection->regions.empty()) {
1368 _session->set_object_selection (selection->regions.start(), selection->regions.end_frame());
1370 _session->clear_object_selection ();
1377 Editor::point_selection_changed ()
1379 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1380 (*i)->set_selected_points (selection->points);
1385 Editor::select_all_in_track (Selection::Operation op)
1387 list<Selectable *> touched;
1389 if (!clicked_routeview) {
1393 begin_reversible_selection_op (X_("Select All in Track"));
1395 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1398 case Selection::Toggle:
1399 selection->add (touched);
1401 case Selection::Set:
1402 selection->set (touched);
1404 case Selection::Extend:
1405 /* meaningless, because we're selecting everything */
1407 case Selection::Add:
1408 selection->add (touched);
1412 commit_reversible_selection_op ();
1416 Editor::select_all_internal_edit (Selection::Operation)
1418 bool selected = false;
1420 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1421 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1423 mrv->select_all_notes ();
1428 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(entered_regionview);
1430 mrv->select_all_notes ();
1438 Editor::select_all_objects (Selection::Operation op)
1440 list<Selectable *> touched;
1442 TrackViewList ts = track_views;
1444 if (internal_editing() && select_all_internal_edit(op)) {
1445 return; // Selected notes
1448 for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1449 if ((*iter)->hidden()) {
1452 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1453 selection->add (*iter);
1457 begin_reversible_selection_op (X_("select all"));
1459 case Selection::Add:
1460 selection->add (touched);
1462 case Selection::Toggle:
1463 selection->add (touched);
1465 case Selection::Set:
1466 selection->set (touched);
1468 case Selection::Extend:
1469 /* meaningless, because we're selecting everything */
1472 commit_reversible_selection_op ();
1476 Editor::invert_selection_in_track ()
1478 list<Selectable *> touched;
1480 if (!clicked_routeview) {
1484 begin_reversible_selection_op (X_("Invert Selection in Track"));
1485 clicked_routeview->get_inverted_selectables (*selection, touched);
1486 selection->set (touched);
1487 commit_reversible_selection_op ();
1491 Editor::invert_selection ()
1493 list<Selectable *> touched;
1495 if (internal_editing()) {
1496 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1497 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1499 mrv->invert_selection ();
1505 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1506 if ((*iter)->hidden()) {
1509 (*iter)->get_inverted_selectables (*selection, touched);
1512 begin_reversible_selection_op (X_("Invert Selection"));
1513 selection->set (touched);
1514 commit_reversible_selection_op ();
1517 /** @param start Start time in session frames.
1518 * @param end End time in session frames.
1519 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1520 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1521 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1522 * within the region are already selected.
1525 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1527 list<Selectable*> found;
1529 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1531 if ((*iter)->hidden()) {
1535 (*iter)->get_selectables (start, end, top, bot, found);
1538 if (found.empty()) {
1539 selection->clear_objects();
1540 selection->clear_time ();
1544 if (preserve_if_selected && op != Selection::Toggle) {
1545 list<Selectable*>::iterator i = found.begin();
1546 while (i != found.end() && (*i)->get_selected()) {
1550 if (i == found.end()) {
1555 begin_reversible_selection_op (X_("select all within"));
1557 case Selection::Add:
1558 selection->add (found);
1560 case Selection::Toggle:
1561 selection->toggle (found);
1563 case Selection::Set:
1564 selection->set (found);
1566 case Selection::Extend:
1567 /* not defined yet */
1571 commit_reversible_selection_op ();
1575 Editor::set_selection_from_region ()
1577 if (selection->regions.empty()) {
1581 /* find all the tracks that have selected regions */
1583 set<TimeAxisView*> tracks;
1585 for (RegionSelection::const_iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
1586 tracks.insert (&(*r)->get_time_axis_view());
1590 tvl.insert (tvl.end(), tracks.begin(), tracks.end());
1592 /* select range (this will clear the region selection) */
1594 selection->set (selection->regions.start(), selection->regions.end_frame());
1596 /* and select the tracks */
1598 selection->set (tvl);
1600 if (!Profile->get_sae()) {
1601 set_mouse_mode (Editing::MouseRange, false);
1606 Editor::set_selection_from_punch()
1610 if ((location = _session->locations()->auto_punch_location()) == 0) {
1614 set_selection_from_range (*location);
1618 Editor::set_selection_from_loop()
1622 if ((location = _session->locations()->auto_loop_location()) == 0) {
1625 set_selection_from_range (*location);
1629 Editor::set_selection_from_range (Location& loc)
1631 begin_reversible_selection_op (X_("set selection from range"));
1632 selection->set (loc.start(), loc.end());
1633 commit_reversible_selection_op ();
1635 if (!Profile->get_sae()) {
1636 set_mouse_mode (Editing::MouseRange, false);
1641 Editor::select_all_selectables_using_time_selection ()
1643 list<Selectable *> touched;
1645 if (selection->time.empty()) {
1649 framepos_t start = selection->time[clicked_selection].start;
1650 framepos_t end = selection->time[clicked_selection].end;
1652 if (end - start < 1) {
1658 if (selection->tracks.empty()) {
1661 ts = &selection->tracks;
1664 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1665 if ((*iter)->hidden()) {
1668 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1671 begin_reversible_selection_op (X_("select all from range"));
1672 selection->set (touched);
1673 commit_reversible_selection_op ();
1678 Editor::select_all_selectables_using_punch()
1680 Location* location = _session->locations()->auto_punch_location();
1681 list<Selectable *> touched;
1683 if (location == 0 || (location->end() - location->start() <= 1)) {
1690 if (selection->tracks.empty()) {
1693 ts = &selection->tracks;
1696 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1697 if ((*iter)->hidden()) {
1700 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1702 begin_reversible_selection_op (X_("select all from punch"));
1703 selection->set (touched);
1704 commit_reversible_selection_op ();
1709 Editor::select_all_selectables_using_loop()
1711 Location* location = _session->locations()->auto_loop_location();
1712 list<Selectable *> touched;
1714 if (location == 0 || (location->end() - location->start() <= 1)) {
1721 if (selection->tracks.empty()) {
1724 ts = &selection->tracks;
1727 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1728 if ((*iter)->hidden()) {
1731 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1733 begin_reversible_selection_op (X_("select all from loop"));
1734 selection->set (touched);
1735 commit_reversible_selection_op ();
1740 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1744 list<Selectable *> touched;
1747 start = cursor->current_frame();
1748 end = _session->current_end_frame();
1750 if (cursor->current_frame() > 0) {
1752 end = cursor->current_frame() - 1;
1758 if (internal_editing()) {
1759 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1760 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1762 mrv->select_range (start, end);
1769 begin_reversible_selection_op (X_("select all after cursor"));
1771 begin_reversible_selection_op (X_("select all before cursor"));
1776 if (selection->tracks.empty()) {
1779 ts = &selection->tracks;
1782 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1783 if ((*iter)->hidden()) {
1786 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1788 selection->set (touched);
1789 commit_reversible_selection_op ();
1793 Editor::select_all_selectables_using_edit (bool after)
1797 list<Selectable *> touched;
1800 start = get_preferred_edit_position(EDIT_IGNORE_NONE, true);
1801 end = _session->current_end_frame();
1803 if ((end = get_preferred_edit_position(EDIT_IGNORE_NONE, true)) > 1) {
1811 if (internal_editing()) {
1812 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1813 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1814 mrv->select_range (start, end);
1820 begin_reversible_selection_op (X_("select all after edit"));
1822 begin_reversible_selection_op (X_("select all before edit"));
1827 if (selection->tracks.empty()) {
1830 ts = &selection->tracks;
1833 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1834 if ((*iter)->hidden()) {
1837 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1839 selection->set (touched);
1840 commit_reversible_selection_op ();
1844 Editor::select_all_selectables_between (bool within)
1848 list<Selectable *> touched;
1850 if (!get_edit_op_range (start, end)) {
1854 if (internal_editing()) {
1855 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1856 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1857 mrv->select_range (start, end);
1864 if (selection->tracks.empty()) {
1867 ts = &selection->tracks;
1870 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1871 if ((*iter)->hidden()) {
1874 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched, within);
1877 begin_reversible_selection_op (X_("Select all Selectables Between"));
1878 selection->set (touched);
1879 commit_reversible_selection_op ();
1883 Editor::select_range_between ()
1888 if ( !selection->time.empty() ) {
1889 selection->clear_time ();
1892 if (!get_edit_op_range (start, end)) {
1896 begin_reversible_selection_op (X_("Select Range Between"));
1897 set_mouse_mode (MouseRange);
1898 selection->set (start, end);
1899 commit_reversible_selection_op ();
1903 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1908 /* if an explicit range exists, use it */
1910 if ( (mouse_mode == MouseRange || get_smart_mode() ) && !selection->time.empty()) {
1911 /* we know that these are ordered */
1912 start = selection->time.start();
1913 end = selection->time.end_frame();
1921 // if (!mouse_frame (m, ignored)) {
1922 // /* mouse is not in a canvas, try playhead+selected marker.
1923 // this is probably most true when using menus.
1926 // if (selection->markers.empty()) {
1930 // start = selection->markers.front()->position();
1931 // end = _session->audible_frame();
1935 // switch (_edit_point) {
1936 // case EditAtPlayhead:
1937 // if (selection->markers.empty()) {
1938 // /* use mouse + playhead */
1940 // end = _session->audible_frame();
1942 // /* use playhead + selected marker */
1943 // start = _session->audible_frame();
1944 // end = selection->markers.front()->position();
1948 // case EditAtMouse:
1949 // /* use mouse + selected marker */
1950 // if (selection->markers.empty()) {
1952 // end = _session->audible_frame();
1954 // start = selection->markers.front()->position();
1959 // case EditAtSelectedMarker:
1960 // /* use mouse + selected marker */
1961 // if (selection->markers.empty()) {
1963 // MessageDialog win (_("No edit range defined"),
1968 // win.set_secondary_text (
1969 // _("the edit point is Selected Marker\nbut there is no selected marker."));
1972 // win.set_default_response (RESPONSE_CLOSE);
1973 // win.set_position (Gtk::WIN_POS_MOUSE);
1978 // return false; // NO RANGE
1980 // start = selection->markers.front()->position();
1986 // if (start == end) {
1990 // if (start > end) {
1991 // swap (start, end);
1994 /* turn range into one delimited by start...end,
2004 Editor::deselect_all ()
2006 begin_reversible_selection_op (X_("Deselect All"));
2007 selection->clear ();
2008 commit_reversible_selection_op ();
2012 Editor::select_range (framepos_t s, framepos_t e)
2014 begin_reversible_selection_op (X_("Select Range"));
2015 selection->add (clicked_axisview);
2016 selection->time.clear ();
2017 long ret = selection->set (s, e);
2018 commit_reversible_selection_op ();