comment fix
[ardour2.git] / gtk2_ardour / editor_selection.cc
blob3212c93bb76ecbf6ba553cb08fc5811222e9b777
1 /*
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.
20 #include <algorithm>
21 #include <cstdlib>
23 #include "pbd/stacktrace.h"
25 #include "ardour/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29 #include "ardour/midi_region.h"
31 #include "editor.h"
32 #include "actions.h"
33 #include "audio_time_axis.h"
34 #include "audio_region_view.h"
35 #include "audio_streamview.h"
36 #include "automation_line.h"
37 #include "control_point.h"
38 #include "editor_regions.h"
39 #include "editor_cursors.h"
40 #include "midi_region_view.h"
42 #include "i18n.h"
44 using namespace std;
45 using namespace ARDOUR;
46 using namespace PBD;
47 using namespace Gtk;
48 using namespace Glib;
49 using namespace Gtkmm2ext;
50 using namespace Editing;
52 struct TrackViewByPositionSorter
54 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
55 return a->y_position() < b->y_position();
59 bool
60 Editor::extend_selection_to_track (TimeAxisView& view)
62 if (selection->selected (&view)) {
63 /* already selected, do nothing */
64 return false;
67 if (selection->tracks.empty()) {
69 if (!selection->selected (&view)) {
70 selection->set (&view);
71 return true;
72 } else {
73 return false;
77 /* something is already selected, so figure out which range of things to add */
79 TrackViewList to_be_added;
80 TrackViewList sorted = track_views;
81 TrackViewByPositionSorter cmp;
82 bool passed_clicked = false;
83 bool forwards = true;
85 sorted.sort (cmp);
87 if (!selection->selected (&view)) {
88 to_be_added.push_back (&view);
91 /* figure out if we should go forward or backwards */
93 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
95 if ((*i) == &view) {
96 passed_clicked = true;
99 if (selection->selected (*i)) {
100 if (passed_clicked) {
101 forwards = true;
102 } else {
103 forwards = false;
105 break;
109 passed_clicked = false;
111 if (forwards) {
113 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
115 if ((*i) == &view) {
116 passed_clicked = true;
117 continue;
120 if (passed_clicked) {
121 if ((*i)->hidden()) {
122 continue;
124 if (selection->selected (*i)) {
125 break;
126 } else if (!(*i)->hidden()) {
127 to_be_added.push_back (*i);
132 } else {
134 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
136 if ((*r) == &view) {
137 passed_clicked = true;
138 continue;
141 if (passed_clicked) {
143 if ((*r)->hidden()) {
144 continue;
147 if (selection->selected (*r)) {
148 break;
149 } else if (!(*r)->hidden()) {
150 to_be_added.push_back (*r);
156 if (!to_be_added.empty()) {
157 selection->add (to_be_added);
158 return true;
161 return false;
164 void
165 Editor::select_all_tracks ()
167 TrackViewList visible_views;
168 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
169 if ((*i)->marked_for_display()) {
170 visible_views.push_back (*i);
173 selection->set (visible_views);
176 /** Select clicked_axisview, unless there are no currently selected
177 * tracks, in which case nothing will happen unless `force' is true.
179 void
180 Editor::set_selected_track_as_side_effect (Selection::Operation op, bool /*force*/)
182 if (!clicked_axisview) {
183 return;
186 #if 1
187 if (!clicked_routeview) {
188 return;
191 bool had_tracks = !selection->tracks.empty();
192 RouteGroup* group = clicked_routeview->route()->route_group();
193 RouteGroup& arg (_session->all_route_group());
195 switch (op) {
196 case Selection::Toggle:
197 if (selection->selected (clicked_axisview)) {
198 if (arg.is_select() && arg.is_active()) {
199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
200 selection->remove(*i);
202 } else if (group && group->is_active()) {
203 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
204 if ((*i)->route_group() == group)
205 selection->remove(*i);
207 } else {
208 selection->remove (clicked_axisview);
210 } else {
211 if (arg.is_select() && arg.is_active()) {
212 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
213 selection->add(*i);
215 } else if (group && group->is_active()) {
216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217 if ( (*i)->route_group() == group)
218 selection->add(*i);
220 } else {
221 selection->add (clicked_axisview);
224 break;
226 case Selection::Add:
227 if (!had_tracks && arg.is_select() && arg.is_active()) {
228 /* nothing was selected already, and all group is active etc. so use
229 all tracks.
231 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
232 selection->add(*i);
234 } else if (group && group->is_active()) {
235 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 if ((*i)->route_group() == group)
237 selection->add(*i);
239 } else {
240 selection->add (clicked_axisview);
242 break;
244 case Selection::Set:
245 selection->clear();
246 if (!had_tracks && arg.is_select() && arg.is_active()) {
247 /* nothing was selected already, and all group is active etc. so use
248 all tracks.
250 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
251 selection->add(*i);
253 } else if (group && group->is_active()) {
254 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
255 if ((*i)->route_group() == group)
256 selection->add(*i);
258 } else {
259 selection->set (clicked_axisview);
261 break;
263 case Selection::Extend:
264 selection->clear();
265 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
266 break;
269 #else // the older version
271 if (!selection->tracks.empty()) {
272 if (!selection->selected (clicked_axisview)) {
273 selection->add (clicked_axisview);
276 } else if (force) {
277 selection->set (clicked_axisview);
279 #endif
282 void
283 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
285 switch (op) {
286 case Selection::Toggle:
287 if (selection->selected (&view)) {
288 if (!no_remove) {
289 selection->remove (&view);
291 } else {
292 selection->add (&view);
294 break;
296 case Selection::Add:
297 if (!selection->selected (&view)) {
298 selection->add (&view);
300 break;
302 case Selection::Set:
303 selection->set (&view);
304 break;
306 case Selection::Extend:
307 extend_selection_to_track (view);
308 break;
312 void
313 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
315 if (!clicked_routeview) {
316 return;
319 if (!press) {
320 return;
323 set_selected_track (*clicked_routeview, op, no_remove);
326 bool
327 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
329 if (!clicked_control_point) {
330 return false;
333 switch (op) {
334 case Selection::Set:
335 selection->set (clicked_control_point);
336 break;
337 case Selection::Add:
338 selection->add (clicked_control_point);
339 break;
340 case Selection::Toggle:
341 selection->toggle (clicked_control_point);
342 break;
343 case Selection::Extend:
344 /* XXX */
345 break;
348 return true;
351 void
352 Editor::get_onscreen_tracks (TrackViewList& tvl)
354 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
355 if ((*i)->y_position() < _canvas_height) {
356 tvl.push_back (*i);
361 /** Call a slot for a given `basis' track and also for any track that is in the same
362 * active route group with a particular set of properties.
364 * @param sl Slot to call.
365 * @param basis Basis track.
366 * @param prop Properties that active edit groups must share to be included in the map.
369 void
370 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
372 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
374 if (route_basis == 0) {
375 return;
378 set<RouteTimeAxisView*> tracks;
379 tracks.insert (route_basis);
381 RouteGroup* group = route_basis->route()->route_group();
383 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
385 /* the basis is a member of an active route group, with the appropriate
386 properties; find other members */
388 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
389 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
390 if (v && v->route()->route_group() == group) {
391 tracks.insert (v);
396 /* call the slots */
397 uint32_t const sz = tracks.size ();
399 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
400 sl (**i, sz);
404 void
405 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
407 boost::shared_ptr<Playlist> pl;
408 vector<boost::shared_ptr<Region> > results;
409 RegionView* marv;
410 boost::shared_ptr<Track> tr;
412 if ((tr = tv.track()) == 0) {
413 /* bus */
414 return;
417 if (&tv == &basis->get_time_axis_view()) {
418 /* looking in same track as the original */
419 return;
422 if ((pl = tr->playlist()) != 0) {
423 pl->get_equivalent_regions (basis->region(), results);
426 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
427 if ((marv = tv.view()->find_view (*ir)) != 0) {
428 all_equivs->push_back (marv);
433 void
434 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
436 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), basis, &equivalent_regions), &basis->get_time_axis_view(), property);
438 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
440 equivalent_regions.push_back (basis);
443 RegionSelection
444 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
446 RegionSelection equivalent;
448 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
450 vector<RegionView*> eq;
452 mapover_tracks (
453 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
454 &(*i)->get_time_axis_view(), prop
457 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
458 equivalent.add (*j);
461 equivalent.add (*i);
464 return equivalent;
469 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
471 int region_count = 0;
473 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
475 RouteTimeAxisView* tatv;
477 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
479 boost::shared_ptr<Playlist> pl;
480 vector<boost::shared_ptr<Region> > results;
481 RegionView* marv;
482 boost::shared_ptr<Track> tr;
484 if ((tr = tatv->track()) == 0) {
485 /* bus */
486 continue;
489 if ((pl = (tr->playlist())) != 0) {
490 pl->get_region_list_equivalent_regions (region, results);
493 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
494 if ((marv = tatv->view()->find_view (*ir)) != 0) {
495 region_count++;
502 return region_count;
506 bool
507 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
509 vector<RegionView*> all_equivalent_regions;
510 bool commit = false;
512 if (!clicked_regionview || !clicked_routeview) {
513 return false;
516 if (press) {
517 button_release_can_deselect = false;
520 if (op == Selection::Toggle || op == Selection::Set) {
523 switch (op) {
524 case Selection::Toggle:
526 if (selection->selected (clicked_regionview)) {
527 if (press) {
529 /* whatever was clicked was selected already; do nothing here but allow
530 the button release to deselect it
533 button_release_can_deselect = true;
535 } else {
537 if (button_release_can_deselect) {
539 /* just remove this one region, but only on a permitted button release */
541 selection->remove (clicked_regionview);
542 commit = true;
544 /* no more deselect action on button release till a new press
545 finds an already selected object.
548 button_release_can_deselect = false;
552 } else {
554 if (press) {
556 if (selection->selected (clicked_routeview)) {
557 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
558 } else {
559 all_equivalent_regions.push_back (clicked_regionview);
562 /* add all the equivalent regions, but only on button press */
564 if (!all_equivalent_regions.empty()) {
565 commit = true;
568 selection->add (all_equivalent_regions);
571 break;
573 case Selection::Set:
574 if (!selection->selected (clicked_regionview)) {
575 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
576 selection->set (all_equivalent_regions);
577 commit = true;
578 } else {
579 /* no commit necessary: clicked on an already selected region */
580 goto out;
582 break;
584 default:
585 /* silly compiler */
586 break;
589 } else if (op == Selection::Extend) {
591 list<Selectable*> results;
592 framepos_t last_frame;
593 framepos_t first_frame;
594 bool same_track = false;
596 /* 1. find the last selected regionview in the track that was clicked in */
598 last_frame = 0;
599 first_frame = max_framepos;
601 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
602 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
604 if ((*x)->region()->last_frame() > last_frame) {
605 last_frame = (*x)->region()->last_frame();
608 if ((*x)->region()->first_frame() < first_frame) {
609 first_frame = (*x)->region()->first_frame();
612 same_track = true;
616 if (same_track) {
618 /* 2. figure out the boundaries for our search for new objects */
620 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
621 case OverlapNone:
622 if (last_frame < clicked_regionview->region()->first_frame()) {
623 first_frame = last_frame;
624 last_frame = clicked_regionview->region()->last_frame();
625 } else {
626 last_frame = first_frame;
627 first_frame = clicked_regionview->region()->first_frame();
629 break;
631 case OverlapExternal:
632 if (last_frame < clicked_regionview->region()->first_frame()) {
633 first_frame = last_frame;
634 last_frame = clicked_regionview->region()->last_frame();
635 } else {
636 last_frame = first_frame;
637 first_frame = clicked_regionview->region()->first_frame();
639 break;
641 case OverlapInternal:
642 if (last_frame < clicked_regionview->region()->first_frame()) {
643 first_frame = last_frame;
644 last_frame = clicked_regionview->region()->last_frame();
645 } else {
646 last_frame = first_frame;
647 first_frame = clicked_regionview->region()->first_frame();
649 break;
651 case OverlapStart:
652 case OverlapEnd:
653 /* nothing to do except add clicked region to selection, since it
654 overlaps with the existing selection in this track.
656 break;
659 } else {
661 /* click in a track that has no regions selected, so extend vertically
662 to pick out all regions that are defined by the existing selection
663 plus this one.
667 first_frame = clicked_regionview->region()->position();
668 last_frame = clicked_regionview->region()->last_frame();
670 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
671 if ((*i)->region()->position() < first_frame) {
672 first_frame = (*i)->region()->position();
674 if ((*i)->region()->last_frame() + 1 > last_frame) {
675 last_frame = (*i)->region()->last_frame();
680 /* 2. find all the tracks we should select in */
682 set<RouteTimeAxisView*> relevant_tracks;
684 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
685 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
686 if (r) {
687 relevant_tracks.insert (r);
691 set<RouteTimeAxisView*> already_in_selection;
693 if (relevant_tracks.empty()) {
695 /* no tracks selected .. thus .. if the
696 regionview we're in isn't selected
697 (i.e. we're about to extend to it), then
698 find all tracks between the this one and
699 any selected ones.
702 if (!selection->selected (clicked_regionview)) {
704 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
706 if (rtv) {
708 /* add this track to the ones we will search */
710 relevant_tracks.insert (rtv);
712 /* find the track closest to this one that
713 already a selected region.
716 RouteTimeAxisView* closest = 0;
717 int distance = INT_MAX;
718 int key = rtv->route()->order_key ("editor");
720 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
722 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
724 if (artv && artv != rtv) {
726 pair<set<RouteTimeAxisView*>::iterator,bool> result;
728 result = already_in_selection.insert (artv);
730 if (result.second) {
731 /* newly added to already_in_selection */
733 int d = artv->route()->order_key ("editor");
735 d -= key;
737 if (abs (d) < distance) {
738 distance = abs (d);
739 closest = artv;
745 if (closest) {
747 /* now add all tracks between that one and this one */
749 int okey = closest->route()->order_key ("editor");
751 if (okey > key) {
752 swap (okey, key);
755 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
756 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
757 if (artv && artv != rtv) {
759 int k = artv->route()->order_key ("editor");
761 if (k >= okey && k <= key) {
763 /* in range but don't add it if
764 it already has tracks selected.
765 this avoids odd selection
766 behaviour that feels wrong.
769 if (find (already_in_selection.begin(),
770 already_in_selection.end(),
771 artv) == already_in_selection.end()) {
773 relevant_tracks.insert (artv);
783 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
784 one that was clicked.
787 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
788 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
791 /* 4. convert to a vector of regions */
793 vector<RegionView*> regions;
795 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
796 RegionView* arv;
798 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
799 regions.push_back (arv);
803 if (!regions.empty()) {
804 selection->add (regions);
805 commit = true;
809 out:
810 return commit;
814 void
815 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
817 vector<RegionView*> all_equivalent_regions;
819 get_regions_corresponding_to (region, all_equivalent_regions);
821 if (all_equivalent_regions.empty()) {
822 return;
825 begin_reversible_command (_("set selected regions"));
827 switch (op) {
828 case Selection::Toggle:
829 /* XXX this is not correct */
830 selection->toggle (all_equivalent_regions);
831 break;
832 case Selection::Set:
833 selection->set (all_equivalent_regions);
834 break;
835 case Selection::Extend:
836 selection->add (all_equivalent_regions);
837 break;
838 case Selection::Add:
839 selection->add (all_equivalent_regions);
840 break;
843 commit_reversible_command () ;
846 bool
847 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
849 RegionView* rv;
850 boost::shared_ptr<Region> r (weak_r.lock());
852 if (!r) {
853 return true;
856 if ((rv = sv->find_view (r)) == 0) {
857 return true;
860 /* don't reset the selection if its something other than
861 a single other region.
864 if (selection->regions.size() > 1) {
865 return true;
868 begin_reversible_command (_("set selected regions"));
870 selection->set (rv);
872 commit_reversible_command () ;
874 return true;
877 void
878 Editor::track_selection_changed ()
880 switch (selection->tracks.size()) {
881 case 0:
882 break;
883 default:
884 set_selected_mixer_strip (*(selection->tracks.front()));
885 break;
888 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
890 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
892 (*i)->set_selected (yn);
894 TimeAxisView::Children c = (*i)->get_child_list ();
895 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
896 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
899 if (yn &&
900 ((mouse_mode == MouseRange) ||
901 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
902 (*i)->reshow_selection (selection->time);
903 } else {
904 (*i)->hide_selection ();
908 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
911 void
912 Editor::time_selection_changed ()
914 if (Profile->get_sae()) {
915 return;
918 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
919 (*i)->hide_selection ();
922 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
923 (*i)->show_selection (selection->time);
926 if (selection->time.empty()) {
927 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
928 } else {
929 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
933 /** Set all region actions to have a given sensitivity */
934 void
935 Editor::sensitize_all_region_actions (bool s)
937 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
939 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
940 (*i)->set_sensitive (s);
943 _all_region_actions_sensitized = s;
946 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
947 * This method should be called just before displaying a Region menu. When a Region menu is not
948 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
949 * on entered_regionviews work without having to check sensitivity every time the selection or
950 * entered_regionview changes.
952 * This method also sets up toggle action state as appropriate.
954 void
955 Editor::sensitize_the_right_region_actions ()
957 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
958 sensitize_all_region_actions (false);
959 if (!selection->time.empty()) {
960 _region_actions->get_action("split-region")->set_sensitive (true);
963 return;
965 } else if (mouse_mode != MouseObject) {
966 sensitize_all_region_actions (false);
967 return;
970 /* We get here if we are in Object mode */
972 RegionSelection rs = get_regions_from_selection_and_entered ();
973 sensitize_all_region_actions (!rs.empty ());
975 _ignore_region_action = true;
977 /* Look through the regions that are selected and make notes about what we have got */
979 bool have_audio = false;
980 bool have_midi = false;
981 bool have_locked = false;
982 bool have_unlocked = false;
983 bool have_position_lock_style_audio = false;
984 bool have_position_lock_style_music = false;
985 bool have_muted = false;
986 bool have_unmuted = false;
987 bool have_opaque = false;
988 bool have_non_opaque = false;
989 bool have_not_at_natural_position = false;
990 bool have_envelope_visible = false;
991 bool have_envelope_invisible = false;
992 bool have_envelope_active = false;
993 bool have_envelope_inactive = false;
994 bool have_non_unity_scale_amplitude = false;
996 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
998 boost::shared_ptr<Region> r = (*i)->region ();
999 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1001 if (ar) {
1002 have_audio = true;
1005 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1006 have_midi = true;
1009 if (r->locked()) {
1010 have_locked = true;
1011 } else {
1012 have_unlocked = true;
1015 if (r->position_lock_style() == MusicTime) {
1016 have_position_lock_style_music = true;
1017 } else {
1018 have_position_lock_style_audio = true;
1021 if (r->muted()) {
1022 have_muted = true;
1023 } else {
1024 have_unmuted = true;
1027 if (r->opaque()) {
1028 have_opaque = true;
1029 } else {
1030 have_non_opaque = true;
1033 if (!r->at_natural_position()) {
1034 have_not_at_natural_position = true;
1037 if (ar) {
1038 /* its a bit unfortunate that "envelope visible" is a view-only
1039 property. we have to find the regionview to able to check
1040 its current setting.
1043 have_envelope_invisible = true;
1045 if (*i) {
1046 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1047 if (arv && arv->envelope_visible()) {
1048 have_envelope_visible = true;
1052 if (ar->envelope_active()) {
1053 have_envelope_active = true;
1054 } else {
1055 have_envelope_inactive = true;
1058 if (ar->scale_amplitude() != 1) {
1059 have_non_unity_scale_amplitude = true;
1064 if (rs.size() > 1) {
1065 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1066 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1067 _region_actions->get_action("rename-region")->set_sensitive (false);
1068 } else if (rs.size() == 1) {
1069 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1070 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1073 if (!have_midi) {
1074 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1075 _region_actions->get_action("quantize-region")->set_sensitive (false);
1076 _region_actions->get_action("fork-region")->set_sensitive (false);
1079 if (_edit_point == EditAtMouse) {
1080 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1081 _region_actions->get_action("trim-front")->set_sensitive (false);
1082 _region_actions->get_action("trim-back")->set_sensitive (false);
1083 _region_actions->get_action("split-region")->set_sensitive (false);
1084 _region_actions->get_action("place-transient")->set_sensitive (false);
1087 if (have_audio) {
1089 if (have_envelope_visible && !have_envelope_invisible) {
1090 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1091 } else if (have_envelope_visible && have_envelope_invisible) {
1092 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1095 if (have_envelope_active && !have_envelope_inactive) {
1096 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1097 } else if (have_envelope_active && have_envelope_inactive) {
1098 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1101 } else {
1103 _region_actions->get_action("analyze-region")->set_sensitive (false);
1104 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1105 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1106 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1110 if (!have_non_unity_scale_amplitude || !have_audio) {
1111 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1114 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1115 if (have_locked && have_unlocked) {
1116 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1119 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"))->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1121 if (have_position_lock_style_music && have_position_lock_style_audio) {
1122 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1125 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1126 if (have_muted && have_unmuted) {
1127 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1130 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1131 if (have_opaque && have_non_opaque) {
1132 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1135 if (!have_not_at_natural_position) {
1136 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1139 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1140 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1141 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1142 } else {
1143 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1146 _ignore_region_action = false;
1148 _all_region_actions_sensitized = false;
1152 void
1153 Editor::region_selection_changed ()
1155 _regions->block_change_connection (true);
1156 editor_regions_selection_changed_connection.block(true);
1158 if (_region_selection_change_updates_region_list) {
1159 _regions->unselect_all ();
1162 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1163 (*i)->set_selected_regionviews (selection->regions);
1166 if (_region_selection_change_updates_region_list) {
1167 _regions->set_selected (selection->regions);
1170 _regions->block_change_connection (false);
1171 editor_regions_selection_changed_connection.block(false);
1173 if (!_all_region_actions_sensitized) {
1174 /* This selection change might have changed what region actions
1175 are allowed, so sensitize them all in case a key is pressed.
1177 sensitize_all_region_actions (true);
1181 void
1182 Editor::point_selection_changed ()
1184 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1185 (*i)->set_selected_points (selection->points);
1189 void
1190 Editor::select_all_in_track (Selection::Operation op)
1192 list<Selectable *> touched;
1194 if (!clicked_routeview) {
1195 return;
1198 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1200 switch (op) {
1201 case Selection::Toggle:
1202 selection->add (touched);
1203 break;
1204 case Selection::Set:
1205 selection->set (touched);
1206 break;
1207 case Selection::Extend:
1208 /* meaningless, because we're selecting everything */
1209 break;
1210 case Selection::Add:
1211 selection->add (touched);
1212 break;
1216 void
1217 Editor::select_all_internal_edit (Selection::Operation op)
1219 /* currently limited to MIDI only */
1221 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
1222 MidiRegionView* mrv = *i;
1223 mrv->select_all_notes ();
1227 void
1228 Editor::select_all (Selection::Operation op)
1230 list<Selectable *> touched;
1232 if (_internal_editing) {
1233 select_all_internal_edit (op);
1234 return;
1237 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1238 if ((*iter)->hidden()) {
1239 continue;
1241 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1243 begin_reversible_command (_("select all"));
1244 switch (op) {
1245 case Selection::Add:
1246 selection->add (touched);
1247 break;
1248 case Selection::Toggle:
1249 selection->add (touched);
1250 break;
1251 case Selection::Set:
1252 selection->set (touched);
1253 break;
1254 case Selection::Extend:
1255 /* meaningless, because we're selecting everything */
1256 break;
1258 commit_reversible_command ();
1260 void
1261 Editor::invert_selection_in_track ()
1263 list<Selectable *> touched;
1265 if (!clicked_routeview) {
1266 return;
1269 clicked_routeview->get_inverted_selectables (*selection, touched);
1270 selection->set (touched);
1273 void
1274 Editor::invert_selection ()
1276 list<Selectable *> touched;
1278 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1279 if ((*iter)->hidden()) {
1280 continue;
1282 (*iter)->get_inverted_selectables (*selection, touched);
1285 selection->set (touched);
1288 /** @param start Start time in session frames.
1289 * @param end End time in session frames.
1290 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1291 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1292 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1293 * within the region are already selected.
1295 void
1296 Editor::select_all_within (
1297 framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected
1300 list<Selectable*> found;
1302 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1304 if ((*iter)->hidden()) {
1305 continue;
1308 (*iter)->get_selectables (start, end, top, bot, found);
1311 if (found.empty()) {
1312 return;
1315 if (preserve_if_selected && op != Selection::Toggle) {
1316 list<Selectable*>::iterator i = found.begin();
1317 while (i != found.end() && (*i)->get_selected()) {
1318 ++i;
1321 if (i == found.end()) {
1322 return;
1326 begin_reversible_command (_("select all within"));
1327 switch (op) {
1328 case Selection::Add:
1329 selection->add (found);
1330 break;
1331 case Selection::Toggle:
1332 selection->toggle (found);
1333 break;
1334 case Selection::Set:
1335 selection->set (found);
1336 break;
1337 case Selection::Extend:
1338 /* not defined yet */
1339 break;
1342 commit_reversible_command ();
1345 void
1346 Editor::set_selection_from_region ()
1348 if (selection->regions.empty()) {
1349 return;
1352 selection->set (selection->regions.start(), selection->regions.end_frame());
1353 if (!Profile->get_sae()) {
1354 set_mouse_mode (Editing::MouseRange, false);
1358 void
1359 Editor::set_selection_from_punch()
1361 Location* location;
1363 if ((location = _session->locations()->auto_punch_location()) == 0) {
1364 return;
1367 set_selection_from_range (*location);
1370 void
1371 Editor::set_selection_from_loop()
1373 Location* location;
1375 if ((location = _session->locations()->auto_loop_location()) == 0) {
1376 return;
1378 set_selection_from_range (*location);
1381 void
1382 Editor::set_selection_from_range (Location& loc)
1384 begin_reversible_command (_("set selection from range"));
1385 selection->set (loc.start(), loc.end());
1386 commit_reversible_command ();
1388 if (!Profile->get_sae()) {
1389 set_mouse_mode (Editing::MouseRange, false);
1393 void
1394 Editor::select_all_selectables_using_time_selection ()
1396 list<Selectable *> touched;
1398 if (selection->time.empty()) {
1399 return;
1402 framepos_t start = selection->time[clicked_selection].start;
1403 framepos_t end = selection->time[clicked_selection].end;
1405 if (end - start < 1) {
1406 return;
1409 TrackViewList* ts;
1411 if (selection->tracks.empty()) {
1412 ts = &track_views;
1413 } else {
1414 ts = &selection->tracks;
1417 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1418 if ((*iter)->hidden()) {
1419 continue;
1421 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1424 begin_reversible_command (_("select all from range"));
1425 selection->set (touched);
1426 commit_reversible_command ();
1430 void
1431 Editor::select_all_selectables_using_punch()
1433 Location* location = _session->locations()->auto_punch_location();
1434 list<Selectable *> touched;
1436 if (location == 0 || (location->end() - location->start() <= 1)) {
1437 return;
1441 TrackViewList* ts;
1443 if (selection->tracks.empty()) {
1444 ts = &track_views;
1445 } else {
1446 ts = &selection->tracks;
1449 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1450 if ((*iter)->hidden()) {
1451 continue;
1453 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1455 begin_reversible_command (_("select all from punch"));
1456 selection->set (touched);
1457 commit_reversible_command ();
1461 void
1462 Editor::select_all_selectables_using_loop()
1464 Location* location = _session->locations()->auto_loop_location();
1465 list<Selectable *> touched;
1467 if (location == 0 || (location->end() - location->start() <= 1)) {
1468 return;
1472 TrackViewList* ts;
1474 if (selection->tracks.empty()) {
1475 ts = &track_views;
1476 } else {
1477 ts = &selection->tracks;
1480 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1481 if ((*iter)->hidden()) {
1482 continue;
1484 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1486 begin_reversible_command (_("select all from loop"));
1487 selection->set (touched);
1488 commit_reversible_command ();
1492 void
1493 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1495 framepos_t start;
1496 framepos_t end;
1497 list<Selectable *> touched;
1499 if (after) {
1500 begin_reversible_command (_("select all after cursor"));
1501 start = cursor->current_frame;
1502 end = _session->current_end_frame();
1503 } else {
1504 if (cursor->current_frame > 0) {
1505 begin_reversible_command (_("select all before cursor"));
1506 start = 0;
1507 end = cursor->current_frame - 1;
1508 } else {
1509 return;
1514 TrackViewList* ts;
1516 if (selection->tracks.empty()) {
1517 ts = &track_views;
1518 } else {
1519 ts = &selection->tracks;
1522 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1523 if ((*iter)->hidden()) {
1524 continue;
1526 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1528 selection->set (touched);
1529 commit_reversible_command ();
1532 void
1533 Editor::select_all_selectables_using_edit (bool after)
1535 framepos_t start;
1536 framepos_t end;
1537 list<Selectable *> touched;
1539 if (after) {
1540 begin_reversible_command (_("select all after edit"));
1541 start = get_preferred_edit_position();
1542 end = _session->current_end_frame();
1543 } else {
1544 if ((end = get_preferred_edit_position()) > 1) {
1545 begin_reversible_command (_("select all before edit"));
1546 start = 0;
1547 end -= 1;
1548 } else {
1549 return;
1554 TrackViewList* ts;
1556 if (selection->tracks.empty()) {
1557 ts = &track_views;
1558 } else {
1559 ts = &selection->tracks;
1562 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1563 if ((*iter)->hidden()) {
1564 continue;
1566 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1568 selection->set (touched);
1569 commit_reversible_command ();
1572 void
1573 Editor::select_all_selectables_between (bool /*within*/)
1575 framepos_t start;
1576 framepos_t end;
1577 list<Selectable *> touched;
1579 if (!get_edit_op_range (start, end)) {
1580 return;
1583 TrackViewList* ts;
1585 if (selection->tracks.empty()) {
1586 ts = &track_views;
1587 } else {
1588 ts = &selection->tracks;
1591 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1592 if ((*iter)->hidden()) {
1593 continue;
1595 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1598 selection->set (touched);
1601 void
1602 Editor::select_range_between ()
1604 framepos_t start;
1605 framepos_t end;
1607 if (mouse_mode == MouseRange && !selection->time.empty()) {
1608 selection->clear_time ();
1611 if (!get_edit_op_range (start, end)) {
1612 return;
1615 set_mouse_mode (MouseRange);
1616 selection->set (start, end);
1619 bool
1620 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1622 framepos_t m;
1623 bool ignored;
1625 /* in range mode, use any existing selection */
1627 if (mouse_mode == MouseRange && !selection->time.empty()) {
1628 /* we know that these are ordered */
1629 start = selection->time.start();
1630 end = selection->time.end_frame();
1631 return true;
1634 if (!mouse_frame (m, ignored)) {
1635 /* mouse is not in a canvas, try playhead+selected marker.
1636 this is probably most true when using menus.
1639 if (selection->markers.empty()) {
1640 return false;
1643 start = selection->markers.front()->position();
1644 end = _session->audible_frame();
1646 } else {
1648 switch (_edit_point) {
1649 case EditAtPlayhead:
1650 if (selection->markers.empty()) {
1651 /* use mouse + playhead */
1652 start = m;
1653 end = _session->audible_frame();
1654 } else {
1655 /* use playhead + selected marker */
1656 start = _session->audible_frame();
1657 end = selection->markers.front()->position();
1659 break;
1661 case EditAtMouse:
1662 /* use mouse + selected marker */
1663 if (selection->markers.empty()) {
1664 start = m;
1665 end = _session->audible_frame();
1666 } else {
1667 start = selection->markers.front()->position();
1668 end = m;
1670 break;
1672 case EditAtSelectedMarker:
1673 /* use mouse + selected marker */
1674 if (selection->markers.empty()) {
1676 MessageDialog win (_("No edit range defined"),
1677 false,
1678 MESSAGE_INFO,
1679 BUTTONS_OK);
1681 win.set_secondary_text (
1682 _("the edit point is Selected Marker\nbut there is no selected marker."));
1685 win.set_default_response (RESPONSE_CLOSE);
1686 win.set_position (Gtk::WIN_POS_MOUSE);
1687 win.show_all();
1689 win.run ();
1691 return false; // NO RANGE
1693 start = selection->markers.front()->position();
1694 end = m;
1695 break;
1699 if (start == end) {
1700 return false;
1703 if (start > end) {
1704 swap (start, end);
1707 /* turn range into one delimited by start...end,
1708 not start...end-1
1711 end++;
1713 return true;
1716 void
1717 Editor::deselect_all ()
1719 selection->clear ();
1722 long
1723 Editor::select_range_around_region (RegionView* rv)
1725 assert (rv);
1727 selection->set (&rv->get_time_axis_view());
1729 selection->time.clear ();
1730 boost::shared_ptr<Region> r = rv->region ();
1731 return selection->set (r->position(), r->position() + r->length());