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 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1151 _region_actions->get_action("quantize-region")->set_sensitive (false);
1152 _region_actions->get_action("fork-region")->set_sensitive (false);
1153 _region_actions->get_action("transpose-region")->set_sensitive (false);
1156 if (_edit_point == EditAtMouse) {
1157 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1158 _region_actions->get_action("trim-front")->set_sensitive (false);
1159 _region_actions->get_action("trim-back")->set_sensitive (false);
1160 _region_actions->get_action("split-region")->set_sensitive (false);
1161 _region_actions->get_action("place-transient")->set_sensitive (false);
1164 if (have_compound_regions) {
1165 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1167 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1172 if (have_envelope_visible && !have_envelope_invisible) {
1173 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1174 } else if (have_envelope_visible && have_envelope_invisible) {
1175 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1178 if (have_envelope_active && !have_envelope_inactive) {
1179 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1180 } else if (have_envelope_active && have_envelope_inactive) {
1181 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1186 _region_actions->get_action("analyze-region")->set_sensitive (false);
1187 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1188 _region_actions->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1189 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1190 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1194 if (!have_non_unity_scale_amplitude || !have_audio) {
1195 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1198 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"))->set_active (have_locked && !have_unlocked);
1199 if (have_locked && have_unlocked) {
1200 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1203 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);
1205 if (have_position_lock_style_music && have_position_lock_style_audio) {
1206 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1209 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"))->set_active (have_muted && !have_unmuted);
1210 if (have_muted && have_unmuted) {
1211 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1214 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"))->set_active (have_opaque && !have_non_opaque);
1215 if (have_opaque && have_non_opaque) {
1216 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1219 if (!have_not_at_natural_position) {
1220 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1223 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1224 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1225 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1227 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1230 _ignore_region_action = false;
1232 _all_region_actions_sensitized = false;
1237 Editor::region_selection_changed ()
1239 _regions->block_change_connection (true);
1240 editor_regions_selection_changed_connection.block(true);
1242 if (_region_selection_change_updates_region_list) {
1243 _regions->unselect_all ();
1246 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1247 (*i)->set_selected_regionviews (selection->regions);
1250 if (_region_selection_change_updates_region_list) {
1251 _regions->set_selected (selection->regions);
1254 _regions->block_change_connection (false);
1255 editor_regions_selection_changed_connection.block(false);
1257 if (!_all_region_actions_sensitized) {
1258 /* This selection change might have changed what region actions
1259 are allowed, so sensitize them all in case a key is pressed.
1261 sensitize_all_region_actions (true);
1266 Editor::point_selection_changed ()
1268 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1269 (*i)->set_selected_points (selection->points);
1274 Editor::select_all_in_track (Selection::Operation op)
1276 list<Selectable *> touched;
1278 if (!clicked_routeview) {
1282 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1285 case Selection::Toggle:
1286 selection->add (touched);
1288 case Selection::Set:
1289 selection->set (touched);
1291 case Selection::Extend:
1292 /* meaningless, because we're selecting everything */
1294 case Selection::Add:
1295 selection->add (touched);
1301 Editor::select_all_internal_edit (Selection::Operation)
1303 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1304 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1306 mrv->select_all_notes ();
1312 Editor::select_all (Selection::Operation op)
1314 list<Selectable *> touched;
1316 if (_internal_editing) {
1317 select_all_internal_edit (op);
1321 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1322 if ((*iter)->hidden()) {
1325 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1327 begin_reversible_command (_("select all"));
1329 case Selection::Add:
1330 selection->add (touched);
1332 case Selection::Toggle:
1333 selection->add (touched);
1335 case Selection::Set:
1336 selection->set (touched);
1338 case Selection::Extend:
1339 /* meaningless, because we're selecting everything */
1342 commit_reversible_command ();
1346 Editor::invert_selection_in_track ()
1348 list<Selectable *> touched;
1350 if (!clicked_routeview) {
1354 clicked_routeview->get_inverted_selectables (*selection, touched);
1355 selection->set (touched);
1359 Editor::invert_selection ()
1361 list<Selectable *> touched;
1363 if (_internal_editing) {
1364 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1365 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1367 mrv->invert_selection ();
1373 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1374 if ((*iter)->hidden()) {
1377 (*iter)->get_inverted_selectables (*selection, touched);
1380 selection->set (touched);
1383 /** @param start Start time in session frames.
1384 * @param end End time in session frames.
1385 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1386 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1387 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1388 * within the region are already selected.
1391 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1393 list<Selectable*> found;
1395 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1397 if ((*iter)->hidden()) {
1401 (*iter)->get_selectables (start, end, top, bot, found);
1404 if (found.empty()) {
1408 if (preserve_if_selected && op != Selection::Toggle) {
1409 list<Selectable*>::iterator i = found.begin();
1410 while (i != found.end() && (*i)->get_selected()) {
1414 if (i == found.end()) {
1419 begin_reversible_command (_("select all within"));
1421 case Selection::Add:
1422 selection->add (found);
1424 case Selection::Toggle:
1425 selection->toggle (found);
1427 case Selection::Set:
1428 selection->set (found);
1430 case Selection::Extend:
1431 /* not defined yet */
1435 commit_reversible_command ();
1439 Editor::set_selection_from_region ()
1441 if (selection->regions.empty()) {
1445 selection->set (selection->regions.start(), selection->regions.end_frame());
1446 if (!Profile->get_sae()) {
1447 set_mouse_mode (Editing::MouseRange, false);
1452 Editor::set_selection_from_punch()
1456 if ((location = _session->locations()->auto_punch_location()) == 0) {
1460 set_selection_from_range (*location);
1464 Editor::set_selection_from_loop()
1468 if ((location = _session->locations()->auto_loop_location()) == 0) {
1471 set_selection_from_range (*location);
1475 Editor::set_selection_from_range (Location& loc)
1477 begin_reversible_command (_("set selection from range"));
1478 selection->set (loc.start(), loc.end());
1479 commit_reversible_command ();
1481 if (!Profile->get_sae()) {
1482 set_mouse_mode (Editing::MouseRange, false);
1487 Editor::select_all_selectables_using_time_selection ()
1489 list<Selectable *> touched;
1491 if (selection->time.empty()) {
1495 framepos_t start = selection->time[clicked_selection].start;
1496 framepos_t end = selection->time[clicked_selection].end;
1498 if (end - start < 1) {
1504 if (selection->tracks.empty()) {
1507 ts = &selection->tracks;
1510 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1511 if ((*iter)->hidden()) {
1514 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1517 begin_reversible_command (_("select all from range"));
1518 selection->set (touched);
1519 commit_reversible_command ();
1524 Editor::select_all_selectables_using_punch()
1526 Location* location = _session->locations()->auto_punch_location();
1527 list<Selectable *> touched;
1529 if (location == 0 || (location->end() - location->start() <= 1)) {
1536 if (selection->tracks.empty()) {
1539 ts = &selection->tracks;
1542 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1543 if ((*iter)->hidden()) {
1546 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1548 begin_reversible_command (_("select all from punch"));
1549 selection->set (touched);
1550 commit_reversible_command ();
1555 Editor::select_all_selectables_using_loop()
1557 Location* location = _session->locations()->auto_loop_location();
1558 list<Selectable *> touched;
1560 if (location == 0 || (location->end() - location->start() <= 1)) {
1567 if (selection->tracks.empty()) {
1570 ts = &selection->tracks;
1573 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1574 if ((*iter)->hidden()) {
1577 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1579 begin_reversible_command (_("select all from loop"));
1580 selection->set (touched);
1581 commit_reversible_command ();
1586 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1590 list<Selectable *> touched;
1593 start = cursor->current_frame;
1594 end = _session->current_end_frame();
1596 if (cursor->current_frame > 0) {
1598 end = cursor->current_frame - 1;
1604 if (_internal_editing) {
1605 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1606 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1608 mrv->select_range (start, end);
1615 begin_reversible_command (_("select all after cursor"));
1617 begin_reversible_command (_("select all before cursor"));
1622 if (selection->tracks.empty()) {
1625 ts = &selection->tracks;
1628 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1629 if ((*iter)->hidden()) {
1632 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1634 selection->set (touched);
1635 commit_reversible_command ();
1639 Editor::select_all_selectables_using_edit (bool after)
1643 list<Selectable *> touched;
1646 start = get_preferred_edit_position();
1647 end = _session->current_end_frame();
1649 if ((end = get_preferred_edit_position()) > 1) {
1657 if (_internal_editing) {
1658 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1659 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1660 mrv->select_range (start, end);
1666 begin_reversible_command (_("select all after edit"));
1668 begin_reversible_command (_("select all before edit"));
1673 if (selection->tracks.empty()) {
1676 ts = &selection->tracks;
1679 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1680 if ((*iter)->hidden()) {
1683 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1685 selection->set (touched);
1686 commit_reversible_command ();
1690 Editor::select_all_selectables_between (bool /*within*/)
1694 list<Selectable *> touched;
1696 if (!get_edit_op_range (start, end)) {
1700 if (_internal_editing) {
1701 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1702 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1703 mrv->select_range (start, end);
1710 if (selection->tracks.empty()) {
1713 ts = &selection->tracks;
1716 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1717 if ((*iter)->hidden()) {
1720 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1723 selection->set (touched);
1727 Editor::select_range_between ()
1732 if (mouse_mode == MouseRange && !selection->time.empty()) {
1733 selection->clear_time ();
1736 if (!get_edit_op_range (start, end)) {
1740 set_mouse_mode (MouseRange);
1741 selection->set (start, end);
1745 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1750 /* in range mode, use any existing selection */
1752 if (mouse_mode == MouseRange && !selection->time.empty()) {
1753 /* we know that these are ordered */
1754 start = selection->time.start();
1755 end = selection->time.end_frame();
1759 if (!mouse_frame (m, ignored)) {
1760 /* mouse is not in a canvas, try playhead+selected marker.
1761 this is probably most true when using menus.
1764 if (selection->markers.empty()) {
1768 start = selection->markers.front()->position();
1769 end = _session->audible_frame();
1773 switch (_edit_point) {
1774 case EditAtPlayhead:
1775 if (selection->markers.empty()) {
1776 /* use mouse + playhead */
1778 end = _session->audible_frame();
1780 /* use playhead + selected marker */
1781 start = _session->audible_frame();
1782 end = selection->markers.front()->position();
1787 /* use mouse + selected marker */
1788 if (selection->markers.empty()) {
1790 end = _session->audible_frame();
1792 start = selection->markers.front()->position();
1797 case EditAtSelectedMarker:
1798 /* use mouse + selected marker */
1799 if (selection->markers.empty()) {
1801 MessageDialog win (_("No edit range defined"),
1806 win.set_secondary_text (
1807 _("the edit point is Selected Marker\nbut there is no selected marker."));
1810 win.set_default_response (RESPONSE_CLOSE);
1811 win.set_position (Gtk::WIN_POS_MOUSE);
1816 return false; // NO RANGE
1818 start = selection->markers.front()->position();
1832 /* turn range into one delimited by start...end,
1842 Editor::deselect_all ()
1844 selection->clear ();
1848 Editor::select_range_around_region (RegionView* rv)
1852 selection->set (&rv->get_time_axis_view());
1854 selection->time.clear ();
1855 boost::shared_ptr<Region> r = rv->region ();
1856 return selection->set (r->position(), r->position() + r->length());