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/session.h"
26 #include "ardour/playlist.h"
27 #include "ardour/route_group.h"
28 #include "ardour/profile.h"
29 #include "ardour/midi_region.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"
45 using namespace ARDOUR;
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();
60 Editor::extend_selection_to_track (TimeAxisView& view)
62 if (selection->selected (&view)) {
63 /* already selected, do nothing */
67 if (selection->tracks.empty()) {
69 if (!selection->selected (&view)) {
70 selection->set (&view);
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;
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) {
96 passed_clicked = true;
99 if (selection->selected (*i)) {
100 if (passed_clicked) {
109 passed_clicked = false;
113 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
116 passed_clicked = true;
120 if (passed_clicked) {
121 if ((*i)->hidden()) {
124 if (selection->selected (*i)) {
126 } else if (!(*i)->hidden()) {
127 to_be_added.push_back (*i);
134 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
137 passed_clicked = true;
141 if (passed_clicked) {
143 if ((*r)->hidden()) {
147 if (selection->selected (*r)) {
149 } else if (!(*r)->hidden()) {
150 to_be_added.push_back (*r);
156 if (!to_be_added.empty()) {
157 selection->add (to_be_added);
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.
180 Editor::set_selected_track_as_side_effect (Selection::Operation op, bool /*force*/)
182 if (!clicked_axisview) {
187 if (!clicked_routeview) {
191 bool had_tracks = !selection->tracks.empty();
192 RouteGroup* group = clicked_routeview->route()->route_group();
193 RouteGroup& arg (_session->all_route_group());
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);
208 selection->remove (clicked_axisview);
211 if (arg.is_select() && arg.is_active()) {
212 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++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)
221 selection->add (clicked_axisview);
227 if (!had_tracks && arg.is_select() && arg.is_active()) {
228 /* nothing was selected already, and all group is active etc. so use
231 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++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)
240 selection->add (clicked_axisview);
246 if (!had_tracks && arg.is_select() && arg.is_active()) {
247 /* nothing was selected already, and all group is active etc. so use
250 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++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)
259 selection->set (clicked_axisview);
263 case Selection::Extend:
265 cerr << ("Editor::set_selected_track_as_side_effect case Selection::Add not yet implemented\n");
269 #else // the older version
271 if (!selection->tracks.empty()) {
272 if (!selection->selected (clicked_axisview)) {
273 selection->add (clicked_axisview);
277 selection->set (clicked_axisview);
283 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
286 case Selection::Toggle:
287 if (selection->selected (&view)) {
289 selection->remove (&view);
292 selection->add (&view);
297 if (!selection->selected (&view)) {
298 selection->add (&view);
303 selection->set (&view);
306 case Selection::Extend:
307 extend_selection_to_track (view);
313 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
315 if (!clicked_routeview) {
323 set_selected_track (*clicked_routeview, op, no_remove);
327 Editor::set_selected_control_point_from_click (Selection::Operation op, bool /*no_remove*/)
329 if (!clicked_control_point) {
335 selection->set (clicked_control_point);
338 selection->add (clicked_control_point);
340 case Selection::Toggle:
341 selection->toggle (clicked_control_point);
343 case Selection::Extend:
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) {
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.
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) {
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) {
397 uint32_t const sz = tracks.size ();
399 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
404 /** Call a slot for a given `basis' track and also for any track that is in the same
405 * active route group with a particular set of properties.
407 * @param sl Slot to call.
408 * @param basis Basis track.
409 * @param prop Properties that active edit groups must share to be included in the map.
413 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
415 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
416 set<boost::shared_ptr<Playlist> > playlists;
418 if (route_basis == 0) {
422 set<RouteTimeAxisView*> tracks;
423 tracks.insert (route_basis);
425 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
427 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
429 /* the basis is a member of an active route group, with the appropriate
430 properties; find other members */
432 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
433 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
435 if (v && v->route()->route_group() == group) {
437 boost::shared_ptr<Track> t = v->track();
439 if (playlists.insert (t->playlist()).second) {
440 /* haven't seen this playlist yet */
444 /* not actually a "Track", but a timeaxis view that
445 we should mapover anyway.
454 uint32_t const sz = tracks.size ();
456 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
462 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
464 boost::shared_ptr<Playlist> pl;
465 vector<boost::shared_ptr<Region> > results;
467 boost::shared_ptr<Track> tr;
469 if ((tr = tv.track()) == 0) {
474 if (&tv == &basis->get_time_axis_view()) {
475 /* looking in same track as the original */
479 if ((pl = tr->playlist()) != 0) {
480 pl->get_equivalent_regions (basis->region(), results);
483 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
484 if ((marv = tv.view()->find_view (*ir)) != 0) {
485 all_equivs->push_back (marv);
491 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
493 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);
495 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
497 equivalent_regions.push_back (basis);
501 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
503 RegionSelection equivalent;
505 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
507 vector<RegionView*> eq;
509 mapover_tracks_with_unique_playlists (
510 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
511 &(*i)->get_time_axis_view(), prop);
513 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
525 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
527 int region_count = 0;
529 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
531 RouteTimeAxisView* tatv;
533 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
535 boost::shared_ptr<Playlist> pl;
536 vector<boost::shared_ptr<Region> > results;
538 boost::shared_ptr<Track> tr;
540 if ((tr = tatv->track()) == 0) {
545 if ((pl = (tr->playlist())) != 0) {
546 pl->get_region_list_equivalent_regions (region, results);
549 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
550 if ((marv = tatv->view()->find_view (*ir)) != 0) {
563 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool /*no_track_remove*/)
565 vector<RegionView*> all_equivalent_regions;
568 if (!clicked_regionview || !clicked_routeview) {
573 button_release_can_deselect = false;
576 if (op == Selection::Toggle || op == Selection::Set) {
580 case Selection::Toggle:
581 if (selection->selected (clicked_regionview)) {
584 /* whatever was clicked was selected already; do nothing here but allow
585 the button release to deselect it
588 button_release_can_deselect = true;
591 if (button_release_can_deselect) {
593 /* just remove this one region, but only on a permitted button release */
595 selection->remove (clicked_regionview);
598 /* no more deselect action on button release till a new press
599 finds an already selected object.
602 button_release_can_deselect = false;
610 if (selection->selected (clicked_routeview)) {
611 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
613 all_equivalent_regions.push_back (clicked_regionview);
616 /* add all the equivalent regions, but only on button press */
618 if (!all_equivalent_regions.empty()) {
622 selection->add (all_equivalent_regions);
628 if (!selection->selected (clicked_regionview)) {
629 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
630 selection->set (all_equivalent_regions);
633 /* no commit necessary: clicked on an already selected region */
643 } else if (op == Selection::Extend) {
645 list<Selectable*> results;
646 framepos_t last_frame;
647 framepos_t first_frame;
648 bool same_track = false;
650 /* 1. find the last selected regionview in the track that was clicked in */
653 first_frame = max_framepos;
655 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
656 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
658 if ((*x)->region()->last_frame() > last_frame) {
659 last_frame = (*x)->region()->last_frame();
662 if ((*x)->region()->first_frame() < first_frame) {
663 first_frame = (*x)->region()->first_frame();
672 /* 2. figure out the boundaries for our search for new objects */
674 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
676 if (last_frame < clicked_regionview->region()->first_frame()) {
677 first_frame = last_frame;
678 last_frame = clicked_regionview->region()->last_frame();
680 last_frame = first_frame;
681 first_frame = clicked_regionview->region()->first_frame();
685 case OverlapExternal:
686 if (last_frame < clicked_regionview->region()->first_frame()) {
687 first_frame = last_frame;
688 last_frame = clicked_regionview->region()->last_frame();
690 last_frame = first_frame;
691 first_frame = clicked_regionview->region()->first_frame();
695 case OverlapInternal:
696 if (last_frame < clicked_regionview->region()->first_frame()) {
697 first_frame = last_frame;
698 last_frame = clicked_regionview->region()->last_frame();
700 last_frame = first_frame;
701 first_frame = clicked_regionview->region()->first_frame();
707 /* nothing to do except add clicked region to selection, since it
708 overlaps with the existing selection in this track.
715 /* click in a track that has no regions selected, so extend vertically
716 to pick out all regions that are defined by the existing selection
721 first_frame = clicked_regionview->region()->position();
722 last_frame = clicked_regionview->region()->last_frame();
724 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
725 if ((*i)->region()->position() < first_frame) {
726 first_frame = (*i)->region()->position();
728 if ((*i)->region()->last_frame() + 1 > last_frame) {
729 last_frame = (*i)->region()->last_frame();
734 /* 2. find all the tracks we should select in */
736 set<RouteTimeAxisView*> relevant_tracks;
738 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
739 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
741 relevant_tracks.insert (r);
745 set<RouteTimeAxisView*> already_in_selection;
747 if (relevant_tracks.empty()) {
749 /* no tracks selected .. thus .. if the
750 regionview we're in isn't selected
751 (i.e. we're about to extend to it), then
752 find all tracks between the this one and
756 if (!selection->selected (clicked_regionview)) {
758 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
762 /* add this track to the ones we will search */
764 relevant_tracks.insert (rtv);
766 /* find the track closest to this one that
767 already a selected region.
770 RouteTimeAxisView* closest = 0;
771 int distance = INT_MAX;
772 int key = rtv->route()->order_key ("editor");
774 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
776 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
778 if (artv && artv != rtv) {
780 pair<set<RouteTimeAxisView*>::iterator,bool> result;
782 result = already_in_selection.insert (artv);
785 /* newly added to already_in_selection */
787 int d = artv->route()->order_key ("editor");
791 if (abs (d) < distance) {
801 /* now add all tracks between that one and this one */
803 int okey = closest->route()->order_key ("editor");
809 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
810 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
811 if (artv && artv != rtv) {
813 int k = artv->route()->order_key ("editor");
815 if (k >= okey && k <= key) {
817 /* in range but don't add it if
818 it already has tracks selected.
819 this avoids odd selection
820 behaviour that feels wrong.
823 if (find (already_in_selection.begin(),
824 already_in_selection.end(),
825 artv) == already_in_selection.end()) {
827 relevant_tracks.insert (artv);
837 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
838 one that was clicked.
841 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
842 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
845 /* 4. convert to a vector of regions */
847 vector<RegionView*> regions;
849 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
852 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
853 regions.push_back (arv);
857 if (!regions.empty()) {
858 selection->add (regions);
869 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
871 vector<RegionView*> all_equivalent_regions;
873 get_regions_corresponding_to (region, all_equivalent_regions);
875 if (all_equivalent_regions.empty()) {
879 begin_reversible_command (_("set selected regions"));
882 case Selection::Toggle:
883 /* XXX this is not correct */
884 selection->toggle (all_equivalent_regions);
887 selection->set (all_equivalent_regions);
889 case Selection::Extend:
890 selection->add (all_equivalent_regions);
893 selection->add (all_equivalent_regions);
897 commit_reversible_command () ;
901 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
904 boost::shared_ptr<Region> r (weak_r.lock());
910 if ((rv = sv->find_view (r)) == 0) {
914 /* don't reset the selection if its something other than
915 a single other region.
918 if (selection->regions.size() > 1) {
922 begin_reversible_command (_("set selected regions"));
926 commit_reversible_command () ;
932 Editor::track_selection_changed ()
934 switch (selection->tracks.size()) {
938 set_selected_mixer_strip (*(selection->tracks.front()));
942 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
944 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
946 (*i)->set_selected (yn);
948 TimeAxisView::Children c = (*i)->get_child_list ();
949 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
950 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
954 ((mouse_mode == MouseRange) ||
955 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
956 (*i)->reshow_selection (selection->time);
958 (*i)->hide_selection ();
962 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
966 Editor::time_selection_changed ()
968 if (Profile->get_sae()) {
972 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
973 (*i)->hide_selection ();
976 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
977 (*i)->show_selection (selection->time);
980 if (selection->time.empty()) {
981 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
983 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
987 /** Set all region actions to have a given sensitivity */
989 Editor::sensitize_all_region_actions (bool s)
991 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
993 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
994 (*i)->set_sensitive (s);
997 _all_region_actions_sensitized = s;
1000 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1001 * This method should be called just before displaying a Region menu. When a Region menu is not
1002 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1003 * on entered_regionviews work without having to check sensitivity every time the selection or
1004 * entered_regionview changes.
1006 * This method also sets up toggle action state as appropriate.
1009 Editor::sensitize_the_right_region_actions ()
1011 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1012 sensitize_all_region_actions (false);
1013 if (!selection->time.empty()) {
1014 _region_actions->get_action("split-region")->set_sensitive (true);
1018 } else if (mouse_mode != MouseObject) {
1019 sensitize_all_region_actions (false);
1023 /* We get here if we are in Object mode */
1025 RegionSelection rs = get_regions_from_selection_and_entered ();
1026 sensitize_all_region_actions (!rs.empty ());
1028 _ignore_region_action = true;
1030 /* Look through the regions that are selected and make notes about what we have got */
1032 bool have_audio = false;
1033 bool have_multichannel_audio = false;
1034 bool have_midi = false;
1035 bool have_locked = false;
1036 bool have_unlocked = false;
1037 bool have_position_lock_style_audio = false;
1038 bool have_position_lock_style_music = false;
1039 bool have_muted = false;
1040 bool have_unmuted = false;
1041 bool have_opaque = false;
1042 bool have_non_opaque = false;
1043 bool have_not_at_natural_position = false;
1044 bool have_envelope_visible = false;
1045 bool have_envelope_invisible = false;
1046 bool have_envelope_active = false;
1047 bool have_envelope_inactive = false;
1048 bool have_non_unity_scale_amplitude = false;
1049 bool have_compound_regions = false;
1051 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1053 boost::shared_ptr<Region> r = (*i)->region ();
1054 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1058 if (ar->n_channels() > 1) {
1059 have_multichannel_audio = true;
1063 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1067 if (r->is_compound()) {
1068 have_compound_regions = true;
1074 have_unlocked = true;
1077 if (r->position_lock_style() == MusicTime) {
1078 have_position_lock_style_music = true;
1080 have_position_lock_style_audio = true;
1086 have_unmuted = true;
1092 have_non_opaque = true;
1095 if (!r->at_natural_position()) {
1096 have_not_at_natural_position = true;
1100 /* its a bit unfortunate that "envelope visible" is a view-only
1101 property. we have to find the regionview to able to check
1102 its current setting.
1105 have_envelope_invisible = false;
1108 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*i);
1110 if (arv->envelope_visible()) {
1111 have_envelope_visible = true;
1113 have_envelope_invisible = true;
1118 if (ar->envelope_active()) {
1119 have_envelope_active = true;
1121 have_envelope_inactive = true;
1124 if (ar->scale_amplitude() != 1) {
1125 have_non_unity_scale_amplitude = true;
1130 if (rs.size() > 1) {
1131 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1132 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1133 _region_actions->get_action("rename-region")->set_sensitive (false);
1135 _region_actions->get_action("combine-regions")->set_sensitive (true);
1137 _region_actions->get_action("combine-regions")->set_sensitive (false);
1139 } else if (rs.size() == 1) {
1140 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1141 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1142 _region_actions->get_action("combine-regions")->set_sensitive (false);
1145 if (!have_multichannel_audio) {
1146 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1150 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1151 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1152 _region_actions->get_action("quantize-region")->set_sensitive (false);
1153 _region_actions->get_action("fork-region")->set_sensitive (false);
1154 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1155 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1156 _region_actions->get_action("transpose-region")->set_sensitive (false);
1158 editor_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1159 /* others were already marked sensitive */
1162 if (_edit_point == EditAtMouse) {
1163 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1164 _region_actions->get_action("trim-front")->set_sensitive (false);
1165 _region_actions->get_action("trim-back")->set_sensitive (false);
1166 _region_actions->get_action("split-region")->set_sensitive (false);
1167 _region_actions->get_action("place-transient")->set_sensitive (false);
1170 if (have_compound_regions) {
1171 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1173 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1178 if (have_envelope_visible && !have_envelope_invisible) {
1179 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1180 } else if (have_envelope_visible && have_envelope_invisible) {
1181 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1184 if (have_envelope_active && !have_envelope_inactive) {
1185 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1186 } else if (have_envelope_active && have_envelope_inactive) {
1187 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1192 _region_actions->get_action("analyze-region")->set_sensitive (false);
1193 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1194 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1195 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1196 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1200 if (!have_non_unity_scale_amplitude || !have_audio) {
1201 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1204 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1205 if (have_locked && have_unlocked) {
1206 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1209 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);
1211 if (have_position_lock_style_music && have_position_lock_style_audio) {
1212 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1215 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1216 if (have_muted && have_unmuted) {
1217 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1220 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1221 if (have_opaque && have_non_opaque) {
1222 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1225 if (!have_not_at_natural_position) {
1226 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1229 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1230 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1231 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1233 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1236 _ignore_region_action = false;
1238 _all_region_actions_sensitized = false;
1243 Editor::region_selection_changed ()
1245 _regions->block_change_connection (true);
1246 editor_regions_selection_changed_connection.block(true);
1248 if (_region_selection_change_updates_region_list) {
1249 _regions->unselect_all ();
1252 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1253 (*i)->set_selected_regionviews (selection->regions);
1256 if (_region_selection_change_updates_region_list) {
1257 _regions->set_selected (selection->regions);
1260 _regions->block_change_connection (false);
1261 editor_regions_selection_changed_connection.block(false);
1263 if (!_all_region_actions_sensitized) {
1264 /* This selection change might have changed what region actions
1265 are allowed, so sensitize them all in case a key is pressed.
1267 sensitize_all_region_actions (true);
1272 Editor::point_selection_changed ()
1274 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1275 (*i)->set_selected_points (selection->points);
1280 Editor::select_all_in_track (Selection::Operation op)
1282 list<Selectable *> touched;
1284 if (!clicked_routeview) {
1288 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1291 case Selection::Toggle:
1292 selection->add (touched);
1294 case Selection::Set:
1295 selection->set (touched);
1297 case Selection::Extend:
1298 /* meaningless, because we're selecting everything */
1300 case Selection::Add:
1301 selection->add (touched);
1307 Editor::select_all_internal_edit (Selection::Operation)
1309 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1310 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1312 mrv->select_all_notes ();
1318 Editor::select_all (Selection::Operation op)
1320 list<Selectable *> touched;
1322 if (_internal_editing) {
1323 select_all_internal_edit (op);
1327 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1328 if ((*iter)->hidden()) {
1331 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1333 begin_reversible_command (_("select all"));
1335 case Selection::Add:
1336 selection->add (touched);
1338 case Selection::Toggle:
1339 selection->add (touched);
1341 case Selection::Set:
1342 selection->set (touched);
1344 case Selection::Extend:
1345 /* meaningless, because we're selecting everything */
1348 commit_reversible_command ();
1352 Editor::invert_selection_in_track ()
1354 list<Selectable *> touched;
1356 if (!clicked_routeview) {
1360 clicked_routeview->get_inverted_selectables (*selection, touched);
1361 selection->set (touched);
1365 Editor::invert_selection ()
1367 list<Selectable *> touched;
1369 if (_internal_editing) {
1370 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1371 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1373 mrv->invert_selection ();
1379 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1380 if ((*iter)->hidden()) {
1383 (*iter)->get_inverted_selectables (*selection, touched);
1386 selection->set (touched);
1389 /** @param start Start time in session frames.
1390 * @param end End time in session frames.
1391 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1392 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1393 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1394 * within the region are already selected.
1397 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1399 list<Selectable*> found;
1401 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1403 if ((*iter)->hidden()) {
1407 (*iter)->get_selectables (start, end, top, bot, found);
1410 if (found.empty()) {
1414 if (preserve_if_selected && op != Selection::Toggle) {
1415 list<Selectable*>::iterator i = found.begin();
1416 while (i != found.end() && (*i)->get_selected()) {
1420 if (i == found.end()) {
1425 begin_reversible_command (_("select all within"));
1427 case Selection::Add:
1428 selection->add (found);
1430 case Selection::Toggle:
1431 selection->toggle (found);
1433 case Selection::Set:
1434 selection->set (found);
1436 case Selection::Extend:
1437 /* not defined yet */
1441 commit_reversible_command ();
1445 Editor::set_selection_from_region ()
1447 if (selection->regions.empty()) {
1451 selection->set (selection->regions.start(), selection->regions.end_frame());
1452 if (!Profile->get_sae()) {
1453 set_mouse_mode (Editing::MouseRange, false);
1458 Editor::set_selection_from_punch()
1462 if ((location = _session->locations()->auto_punch_location()) == 0) {
1466 set_selection_from_range (*location);
1470 Editor::set_selection_from_loop()
1474 if ((location = _session->locations()->auto_loop_location()) == 0) {
1477 set_selection_from_range (*location);
1481 Editor::set_selection_from_range (Location& loc)
1483 begin_reversible_command (_("set selection from range"));
1484 selection->set (loc.start(), loc.end());
1485 commit_reversible_command ();
1487 if (!Profile->get_sae()) {
1488 set_mouse_mode (Editing::MouseRange, false);
1493 Editor::select_all_selectables_using_time_selection ()
1495 list<Selectable *> touched;
1497 if (selection->time.empty()) {
1501 framepos_t start = selection->time[clicked_selection].start;
1502 framepos_t end = selection->time[clicked_selection].end;
1504 if (end - start < 1) {
1510 if (selection->tracks.empty()) {
1513 ts = &selection->tracks;
1516 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1517 if ((*iter)->hidden()) {
1520 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1523 begin_reversible_command (_("select all from range"));
1524 selection->set (touched);
1525 commit_reversible_command ();
1530 Editor::select_all_selectables_using_punch()
1532 Location* location = _session->locations()->auto_punch_location();
1533 list<Selectable *> touched;
1535 if (location == 0 || (location->end() - location->start() <= 1)) {
1542 if (selection->tracks.empty()) {
1545 ts = &selection->tracks;
1548 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1549 if ((*iter)->hidden()) {
1552 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1554 begin_reversible_command (_("select all from punch"));
1555 selection->set (touched);
1556 commit_reversible_command ();
1561 Editor::select_all_selectables_using_loop()
1563 Location* location = _session->locations()->auto_loop_location();
1564 list<Selectable *> touched;
1566 if (location == 0 || (location->end() - location->start() <= 1)) {
1573 if (selection->tracks.empty()) {
1576 ts = &selection->tracks;
1579 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1580 if ((*iter)->hidden()) {
1583 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1585 begin_reversible_command (_("select all from loop"));
1586 selection->set (touched);
1587 commit_reversible_command ();
1592 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1596 list<Selectable *> touched;
1599 start = cursor->current_frame;
1600 end = _session->current_end_frame();
1602 if (cursor->current_frame > 0) {
1604 end = cursor->current_frame - 1;
1610 if (_internal_editing) {
1611 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1612 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1614 mrv->select_range (start, end);
1621 begin_reversible_command (_("select all after cursor"));
1623 begin_reversible_command (_("select all before cursor"));
1628 if (selection->tracks.empty()) {
1631 ts = &selection->tracks;
1634 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1635 if ((*iter)->hidden()) {
1638 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1640 selection->set (touched);
1641 commit_reversible_command ();
1645 Editor::select_all_selectables_using_edit (bool after)
1649 list<Selectable *> touched;
1652 start = get_preferred_edit_position();
1653 end = _session->current_end_frame();
1655 if ((end = get_preferred_edit_position()) > 1) {
1663 if (_internal_editing) {
1664 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1665 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1666 mrv->select_range (start, end);
1672 begin_reversible_command (_("select all after edit"));
1674 begin_reversible_command (_("select all before edit"));
1679 if (selection->tracks.empty()) {
1682 ts = &selection->tracks;
1685 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1686 if ((*iter)->hidden()) {
1689 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1691 selection->set (touched);
1692 commit_reversible_command ();
1696 Editor::select_all_selectables_between (bool /*within*/)
1700 list<Selectable *> touched;
1702 if (!get_edit_op_range (start, end)) {
1706 if (_internal_editing) {
1707 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1708 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1709 mrv->select_range (start, end);
1716 if (selection->tracks.empty()) {
1719 ts = &selection->tracks;
1722 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1723 if ((*iter)->hidden()) {
1726 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1729 selection->set (touched);
1733 Editor::select_range_between ()
1738 if (mouse_mode == MouseRange && !selection->time.empty()) {
1739 selection->clear_time ();
1742 if (!get_edit_op_range (start, end)) {
1746 set_mouse_mode (MouseRange);
1747 selection->set (start, end);
1751 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1756 /* in range mode, use any existing selection */
1758 if (mouse_mode == MouseRange && !selection->time.empty()) {
1759 /* we know that these are ordered */
1760 start = selection->time.start();
1761 end = selection->time.end_frame();
1765 if (!mouse_frame (m, ignored)) {
1766 /* mouse is not in a canvas, try playhead+selected marker.
1767 this is probably most true when using menus.
1770 if (selection->markers.empty()) {
1774 start = selection->markers.front()->position();
1775 end = _session->audible_frame();
1779 switch (_edit_point) {
1780 case EditAtPlayhead:
1781 if (selection->markers.empty()) {
1782 /* use mouse + playhead */
1784 end = _session->audible_frame();
1786 /* use playhead + selected marker */
1787 start = _session->audible_frame();
1788 end = selection->markers.front()->position();
1793 /* use mouse + selected marker */
1794 if (selection->markers.empty()) {
1796 end = _session->audible_frame();
1798 start = selection->markers.front()->position();
1803 case EditAtSelectedMarker:
1804 /* use mouse + selected marker */
1805 if (selection->markers.empty()) {
1807 MessageDialog win (_("No edit range defined"),
1812 win.set_secondary_text (
1813 _("the edit point is Selected Marker\nbut there is no selected marker."));
1816 win.set_default_response (RESPONSE_CLOSE);
1817 win.set_position (Gtk::WIN_POS_MOUSE);
1822 return false; // NO RANGE
1824 start = selection->markers.front()->position();
1838 /* turn range into one delimited by start...end,
1848 Editor::deselect_all ()
1850 selection->clear ();
1854 Editor::select_range_around_region (RegionView* rv)
1858 selection->set (&rv->get_time_axis_view());
1860 selection->time.clear ();
1861 boost::shared_ptr<Region> r = rv->region ();
1862 return selection->set (r->position(), r->position() + r->length());