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
) {
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
;
410 boost::shared_ptr
<Track
> tr
;
412 if ((tr
= tv
.track()) == 0) {
417 if (&tv
== &basis
->get_time_axis_view()) {
418 /* looking in same track as the original */
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
);
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
);
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
;
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
) {
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
;
482 boost::shared_ptr
<Track
> tr
;
484 if ((tr
= tatv
->track()) == 0) {
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) {
507 Editor::set_selected_regionview_from_click (bool press
, Selection::Operation op
, bool /*no_track_remove*/)
509 vector
<RegionView
*> all_equivalent_regions
;
512 if (!clicked_regionview
|| !clicked_routeview
) {
517 button_release_can_deselect
= false;
520 if (op
== Selection::Toggle
|| op
== Selection::Set
) {
524 case Selection::Toggle
:
525 if (selection
->selected (clicked_regionview
)) {
528 /* whatever was clicked was selected already; do nothing here but allow
529 the button release to deselect it
532 button_release_can_deselect
= true;
535 if (button_release_can_deselect
) {
537 /* just remove this one region, but only on a permitted button release */
539 selection
->remove (clicked_regionview
);
542 /* no more deselect action on button release till a new press
543 finds an already selected object.
546 button_release_can_deselect
= false;
554 if (selection
->selected (clicked_routeview
)) {
555 get_equivalent_regions (clicked_regionview
, all_equivalent_regions
, ARDOUR::Properties::select
.property_id
);
557 all_equivalent_regions
.push_back (clicked_regionview
);
560 /* add all the equivalent regions, but only on button press */
562 if (!all_equivalent_regions
.empty()) {
566 selection
->add (all_equivalent_regions
);
572 if (!selection
->selected (clicked_regionview
)) {
573 get_equivalent_regions (clicked_regionview
, all_equivalent_regions
, ARDOUR::Properties::select
.property_id
);
574 selection
->set (all_equivalent_regions
);
577 /* no commit necessary: clicked on an already selected region */
587 } else if (op
== Selection::Extend
) {
589 list
<Selectable
*> results
;
590 framepos_t last_frame
;
591 framepos_t first_frame
;
592 bool same_track
= false;
594 /* 1. find the last selected regionview in the track that was clicked in */
597 first_frame
= max_framepos
;
599 for (RegionSelection::iterator x
= selection
->regions
.begin(); x
!= selection
->regions
.end(); ++x
) {
600 if (&(*x
)->get_time_axis_view() == &clicked_regionview
->get_time_axis_view()) {
602 if ((*x
)->region()->last_frame() > last_frame
) {
603 last_frame
= (*x
)->region()->last_frame();
606 if ((*x
)->region()->first_frame() < first_frame
) {
607 first_frame
= (*x
)->region()->first_frame();
616 /* 2. figure out the boundaries for our search for new objects */
618 switch (clicked_regionview
->region()->coverage (first_frame
, last_frame
)) {
620 if (last_frame
< clicked_regionview
->region()->first_frame()) {
621 first_frame
= last_frame
;
622 last_frame
= clicked_regionview
->region()->last_frame();
624 last_frame
= first_frame
;
625 first_frame
= clicked_regionview
->region()->first_frame();
629 case OverlapExternal
:
630 if (last_frame
< clicked_regionview
->region()->first_frame()) {
631 first_frame
= last_frame
;
632 last_frame
= clicked_regionview
->region()->last_frame();
634 last_frame
= first_frame
;
635 first_frame
= clicked_regionview
->region()->first_frame();
639 case OverlapInternal
:
640 if (last_frame
< clicked_regionview
->region()->first_frame()) {
641 first_frame
= last_frame
;
642 last_frame
= clicked_regionview
->region()->last_frame();
644 last_frame
= first_frame
;
645 first_frame
= clicked_regionview
->region()->first_frame();
651 /* nothing to do except add clicked region to selection, since it
652 overlaps with the existing selection in this track.
659 /* click in a track that has no regions selected, so extend vertically
660 to pick out all regions that are defined by the existing selection
665 first_frame
= clicked_regionview
->region()->position();
666 last_frame
= clicked_regionview
->region()->last_frame();
668 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
669 if ((*i
)->region()->position() < first_frame
) {
670 first_frame
= (*i
)->region()->position();
672 if ((*i
)->region()->last_frame() + 1 > last_frame
) {
673 last_frame
= (*i
)->region()->last_frame();
678 /* 2. find all the tracks we should select in */
680 set
<RouteTimeAxisView
*> relevant_tracks
;
682 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
683 RouteTimeAxisView
* r
= dynamic_cast<RouteTimeAxisView
*> (*i
);
685 relevant_tracks
.insert (r
);
689 set
<RouteTimeAxisView
*> already_in_selection
;
691 if (relevant_tracks
.empty()) {
693 /* no tracks selected .. thus .. if the
694 regionview we're in isn't selected
695 (i.e. we're about to extend to it), then
696 find all tracks between the this one and
700 if (!selection
->selected (clicked_regionview
)) {
702 RouteTimeAxisView
* rtv
= dynamic_cast<RouteTimeAxisView
*> (&clicked_regionview
->get_time_axis_view());
706 /* add this track to the ones we will search */
708 relevant_tracks
.insert (rtv
);
710 /* find the track closest to this one that
711 already a selected region.
714 RouteTimeAxisView
* closest
= 0;
715 int distance
= INT_MAX
;
716 int key
= rtv
->route()->order_key ("editor");
718 for (RegionSelection::iterator x
= selection
->regions
.begin(); x
!= selection
->regions
.end(); ++x
) {
720 RouteTimeAxisView
* artv
= dynamic_cast<RouteTimeAxisView
*>(&(*x
)->get_time_axis_view());
722 if (artv
&& artv
!= rtv
) {
724 pair
<set
<RouteTimeAxisView
*>::iterator
,bool> result
;
726 result
= already_in_selection
.insert (artv
);
729 /* newly added to already_in_selection */
731 int d
= artv
->route()->order_key ("editor");
735 if (abs (d
) < distance
) {
745 /* now add all tracks between that one and this one */
747 int okey
= closest
->route()->order_key ("editor");
753 for (TrackViewList::iterator x
= track_views
.begin(); x
!= track_views
.end(); ++x
) {
754 RouteTimeAxisView
* artv
= dynamic_cast<RouteTimeAxisView
*>(*x
);
755 if (artv
&& artv
!= rtv
) {
757 int k
= artv
->route()->order_key ("editor");
759 if (k
>= okey
&& k
<= key
) {
761 /* in range but don't add it if
762 it already has tracks selected.
763 this avoids odd selection
764 behaviour that feels wrong.
767 if (find (already_in_selection
.begin(),
768 already_in_selection
.end(),
769 artv
) == already_in_selection
.end()) {
771 relevant_tracks
.insert (artv
);
781 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
782 one that was clicked.
785 for (set
<RouteTimeAxisView
*>::iterator t
= relevant_tracks
.begin(); t
!= relevant_tracks
.end(); ++t
) {
786 (*t
)->get_selectables (first_frame
, last_frame
, -1.0, -1.0, results
);
789 /* 4. convert to a vector of regions */
791 vector
<RegionView
*> regions
;
793 for (list
<Selectable
*>::iterator x
= results
.begin(); x
!= results
.end(); ++x
) {
796 if ((arv
= dynamic_cast<RegionView
*>(*x
)) != 0) {
797 regions
.push_back (arv
);
801 if (!regions
.empty()) {
802 selection
->add (regions
);
813 Editor::set_selected_regionview_from_region_list (boost::shared_ptr
<Region
> region
, Selection::Operation op
)
815 vector
<RegionView
*> all_equivalent_regions
;
817 get_regions_corresponding_to (region
, all_equivalent_regions
);
819 if (all_equivalent_regions
.empty()) {
823 begin_reversible_command (_("set selected regions"));
826 case Selection::Toggle
:
827 /* XXX this is not correct */
828 selection
->toggle (all_equivalent_regions
);
831 selection
->set (all_equivalent_regions
);
833 case Selection::Extend
:
834 selection
->add (all_equivalent_regions
);
837 selection
->add (all_equivalent_regions
);
841 commit_reversible_command () ;
845 Editor::set_selected_regionview_from_map_event (GdkEventAny
* /*ev*/, StreamView
* sv
, boost::weak_ptr
<Region
> weak_r
)
848 boost::shared_ptr
<Region
> r (weak_r
.lock());
854 if ((rv
= sv
->find_view (r
)) == 0) {
858 /* don't reset the selection if its something other than
859 a single other region.
862 if (selection
->regions
.size() > 1) {
866 begin_reversible_command (_("set selected regions"));
870 commit_reversible_command () ;
876 Editor::track_selection_changed ()
878 switch (selection
->tracks
.size()) {
882 set_selected_mixer_strip (*(selection
->tracks
.front()));
886 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
888 bool yn
= (find (selection
->tracks
.begin(), selection
->tracks
.end(), *i
) != selection
->tracks
.end());
890 (*i
)->set_selected (yn
);
892 TimeAxisView::Children c
= (*i
)->get_child_list ();
893 for (TimeAxisView::Children::iterator j
= c
.begin(); j
!= c
.end(); ++j
) {
894 (*j
)->set_selected (find (selection
->tracks
.begin(), selection
->tracks
.end(), j
->get()) != selection
->tracks
.end());
898 ((mouse_mode
== MouseRange
) ||
899 ((mouse_mode
== MouseObject
) && (_join_object_range_state
== JOIN_OBJECT_RANGE_OBJECT
)))) {
900 (*i
)->reshow_selection (selection
->time
);
902 (*i
)->hide_selection ();
906 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions
, !selection
->tracks
.empty());
910 Editor::time_selection_changed ()
912 if (Profile
->get_sae()) {
916 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
917 (*i
)->hide_selection ();
920 for (TrackSelection::iterator i
= selection
->tracks
.begin(); i
!= selection
->tracks
.end(); ++i
) {
921 (*i
)->show_selection (selection
->time
);
924 if (selection
->time
.empty()) {
925 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions
, false);
927 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions
, true);
931 /** Set all region actions to have a given sensitivity */
933 Editor::sensitize_all_region_actions (bool s
)
935 Glib::ListHandle
<Glib::RefPtr
<Action
> > all
= _region_actions
->get_actions ();
937 for (Glib::ListHandle
<Glib::RefPtr
<Action
> >::iterator i
= all
.begin(); i
!= all
.end(); ++i
) {
938 (*i
)->set_sensitive (s
);
941 _all_region_actions_sensitized
= s
;
944 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
945 * This method should be called just before displaying a Region menu. When a Region menu is not
946 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
947 * on entered_regionviews work without having to check sensitivity every time the selection or
948 * entered_regionview changes.
950 * This method also sets up toggle action state as appropriate.
953 Editor::sensitize_the_right_region_actions ()
955 if ((mouse_mode
== MouseRange
) || (mouse_mode
!= MouseObject
&& _join_object_range_state
== JOIN_OBJECT_RANGE_RANGE
)) {
956 sensitize_all_region_actions (false);
957 if (!selection
->time
.empty()) {
958 _region_actions
->get_action("split-region")->set_sensitive (true);
962 } else if (mouse_mode
!= MouseObject
) {
963 sensitize_all_region_actions (false);
967 /* We get here if we are in Object mode */
969 RegionSelection rs
= get_regions_from_selection_and_entered ();
970 sensitize_all_region_actions (!rs
.empty ());
972 _ignore_region_action
= true;
974 /* Look through the regions that are selected and make notes about what we have got */
976 bool have_audio
= false;
977 bool have_midi
= false;
978 bool have_locked
= false;
979 bool have_unlocked
= false;
980 bool have_position_lock_style_audio
= false;
981 bool have_position_lock_style_music
= false;
982 bool have_muted
= false;
983 bool have_unmuted
= false;
984 bool have_opaque
= false;
985 bool have_non_opaque
= false;
986 bool have_not_at_natural_position
= false;
987 bool have_envelope_visible
= false;
988 bool have_envelope_invisible
= false;
989 bool have_envelope_active
= false;
990 bool have_envelope_inactive
= false;
991 bool have_non_unity_scale_amplitude
= false;
992 bool have_compound_regions
= false;
994 for (list
<RegionView
*>::const_iterator i
= rs
.begin(); i
!= rs
.end(); ++i
) {
996 boost::shared_ptr
<Region
> r
= (*i
)->region ();
997 boost::shared_ptr
<AudioRegion
> ar
= boost::dynamic_pointer_cast
<AudioRegion
> (r
);
1003 if (boost::dynamic_pointer_cast
<MidiRegion
> (r
)) {
1007 if (r
->is_compound()) {
1008 have_compound_regions
= true;
1014 have_unlocked
= true;
1017 if (r
->position_lock_style() == MusicTime
) {
1018 have_position_lock_style_music
= true;
1020 have_position_lock_style_audio
= true;
1026 have_unmuted
= true;
1032 have_non_opaque
= true;
1035 if (!r
->at_natural_position()) {
1036 have_not_at_natural_position
= true;
1040 /* its a bit unfortunate that "envelope visible" is a view-only
1041 property. we have to find the regionview to able to check
1042 its current setting.
1045 have_envelope_invisible
= true;
1048 AudioRegionView
* arv
= dynamic_cast<AudioRegionView
*> (*i
);
1049 if (arv
&& arv
->envelope_visible()) {
1050 have_envelope_visible
= true;
1054 if (ar
->envelope_active()) {
1055 have_envelope_active
= true;
1057 have_envelope_inactive
= true;
1060 if (ar
->scale_amplitude() != 1) {
1061 have_non_unity_scale_amplitude
= true;
1066 if (rs
.size() > 1) {
1067 _region_actions
->get_action("show-region-list-editor")->set_sensitive (false);
1068 _region_actions
->get_action("show-region-properties")->set_sensitive (false);
1069 _region_actions
->get_action("rename-region")->set_sensitive (false);
1070 _region_actions
->get_action("combine-regions")->set_sensitive (true);
1071 } else if (rs
.size() == 1) {
1072 _region_actions
->get_action("add-range-markers-from-region")->set_sensitive (false);
1073 _region_actions
->get_action("close-region-gaps")->set_sensitive (false);
1074 _region_actions
->get_action("combine-regions")->set_sensitive (false);
1078 _region_actions
->get_action("show-region-list-editor")->set_sensitive (false);
1079 _region_actions
->get_action("quantize-region")->set_sensitive (false);
1080 _region_actions
->get_action("fork-region")->set_sensitive (false);
1081 _region_actions
->get_action("transpose-region")->set_sensitive (false);
1084 if (_edit_point
== EditAtMouse
) {
1085 _region_actions
->get_action("set-region-sync-position")->set_sensitive (false);
1086 _region_actions
->get_action("trim-front")->set_sensitive (false);
1087 _region_actions
->get_action("trim-back")->set_sensitive (false);
1088 _region_actions
->get_action("split-region")->set_sensitive (false);
1089 _region_actions
->get_action("place-transient")->set_sensitive (false);
1092 if (have_compound_regions
) {
1093 _region_actions
->get_action("uncombine-regions")->set_sensitive (true);
1098 if (have_envelope_visible
&& !have_envelope_invisible
) {
1099 Glib::RefPtr
<ToggleAction
>::cast_dynamic (_region_actions
->get_action("toggle-region-gain-envelope-visible"))->set_active ();
1100 } else if (have_envelope_visible
&& have_envelope_invisible
) {
1101 // _region_actions->get_action("toggle-region-gain-envelope-visible")->set_inconsistent ();
1104 if (have_envelope_active
&& !have_envelope_inactive
) {
1105 Glib::RefPtr
<ToggleAction
>::cast_dynamic (_region_actions
->get_action("toggle-region-gain-envelope-active"))->set_active ();
1106 } else if (have_envelope_active
&& have_envelope_inactive
) {
1107 // _region_actions->get_action("toggle-region-gain-envelope-active")->set_inconsistent ();
1112 _region_actions
->get_action("analyze-region")->set_sensitive (false);
1113 _region_actions
->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1114 _region_actions
->get_action("toggle-region-gain-envelope-visible")->set_sensitive (false);
1115 _region_actions
->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1116 _region_actions
->get_action("pitch-shift-region")->set_sensitive (false);
1120 if (!have_non_unity_scale_amplitude
|| !have_audio
) {
1121 _region_actions
->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1124 Glib::RefPtr
<ToggleAction
>::cast_dynamic (_region_actions
->get_action("toggle-region-lock"))->set_active (have_locked
&& !have_unlocked
);
1125 if (have_locked
&& have_unlocked
) {
1126 // _region_actions->get_action("toggle-region-lock")->set_inconsistent ();
1129 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
);
1131 if (have_position_lock_style_music
&& have_position_lock_style_audio
) {
1132 // _region_actions->get_action("toggle-region-lock-style")->set_inconsistent ();
1135 Glib::RefPtr
<ToggleAction
>::cast_dynamic (_region_actions
->get_action("toggle-region-mute"))->set_active (have_muted
&& !have_unmuted
);
1136 if (have_muted
&& have_unmuted
) {
1137 // _region_actions->get_action("toggle-region-mute")->set_inconsistent ();
1140 Glib::RefPtr
<ToggleAction
>::cast_dynamic (_region_actions
->get_action("toggle-opaque-region"))->set_active (have_opaque
&& !have_non_opaque
);
1141 if (have_opaque
&& have_non_opaque
) {
1142 // _region_actions->get_action("toggle-opaque-region")->set_inconsistent ();
1145 if (!have_not_at_natural_position
) {
1146 _region_actions
->get_action("naturalize-region")->set_sensitive (false);
1149 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1150 if (_edit_point
== EditAtMouse
|| _regions
->get_single_selection() == 0 || selection
->tracks
.empty()) {
1151 _region_actions
->get_action("insert-region-from-region-list")->set_sensitive (false);
1153 _region_actions
->get_action("insert-region-from-region-list")->set_sensitive (true);
1156 _ignore_region_action
= false;
1158 _all_region_actions_sensitized
= false;
1163 Editor::region_selection_changed ()
1165 _regions
->block_change_connection (true);
1166 editor_regions_selection_changed_connection
.block(true);
1168 if (_region_selection_change_updates_region_list
) {
1169 _regions
->unselect_all ();
1172 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
1173 (*i
)->set_selected_regionviews (selection
->regions
);
1176 if (_region_selection_change_updates_region_list
) {
1177 _regions
->set_selected (selection
->regions
);
1180 _regions
->block_change_connection (false);
1181 editor_regions_selection_changed_connection
.block(false);
1183 if (!_all_region_actions_sensitized
) {
1184 /* This selection change might have changed what region actions
1185 are allowed, so sensitize them all in case a key is pressed.
1187 sensitize_all_region_actions (true);
1192 Editor::point_selection_changed ()
1194 for (TrackViewList::iterator i
= track_views
.begin(); i
!= track_views
.end(); ++i
) {
1195 (*i
)->set_selected_points (selection
->points
);
1200 Editor::select_all_in_track (Selection::Operation op
)
1202 list
<Selectable
*> touched
;
1204 if (!clicked_routeview
) {
1208 clicked_routeview
->get_selectables (0, max_framepos
, 0, DBL_MAX
, touched
);
1211 case Selection::Toggle
:
1212 selection
->add (touched
);
1214 case Selection::Set
:
1215 selection
->set (touched
);
1217 case Selection::Extend
:
1218 /* meaningless, because we're selecting everything */
1220 case Selection::Add
:
1221 selection
->add (touched
);
1227 Editor::select_all_internal_edit (Selection::Operation op
)
1229 /* currently limited to MIDI only */
1231 for (RegionSelection::iterator i
= selection
->regions
.begin(); i
!= selection
->regions
.end(); ++i
) {
1232 MidiRegionView
* mrv
= dynamic_cast<MidiRegionView
*>(*i
);
1234 mrv
->select_all_notes ();
1240 Editor::select_all (Selection::Operation op
)
1242 list
<Selectable
*> touched
;
1244 if (_internal_editing
) {
1245 select_all_internal_edit (op
);
1249 for (TrackViewList::iterator iter
= track_views
.begin(); iter
!= track_views
.end(); ++iter
) {
1250 if ((*iter
)->hidden()) {
1253 (*iter
)->get_selectables (0, max_framepos
, 0, DBL_MAX
, touched
);
1255 begin_reversible_command (_("select all"));
1257 case Selection::Add
:
1258 selection
->add (touched
);
1260 case Selection::Toggle
:
1261 selection
->add (touched
);
1263 case Selection::Set
:
1264 selection
->set (touched
);
1266 case Selection::Extend
:
1267 /* meaningless, because we're selecting everything */
1270 commit_reversible_command ();
1273 Editor::invert_selection_in_track ()
1275 list
<Selectable
*> touched
;
1277 if (!clicked_routeview
) {
1281 clicked_routeview
->get_inverted_selectables (*selection
, touched
);
1282 selection
->set (touched
);
1286 Editor::invert_selection ()
1288 list
<Selectable
*> touched
;
1290 for (TrackViewList::iterator iter
= track_views
.begin(); iter
!= track_views
.end(); ++iter
) {
1291 if ((*iter
)->hidden()) {
1294 (*iter
)->get_inverted_selectables (*selection
, touched
);
1297 selection
->set (touched
);
1300 /** @param start Start time in session frames.
1301 * @param end End time in session frames.
1302 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1303 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1304 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1305 * within the region are already selected.
1308 Editor::select_all_within (
1309 framepos_t start
, framepos_t end
, double top
, double bot
, const TrackViewList
& tracklist
, Selection::Operation op
, bool preserve_if_selected
1312 list
<Selectable
*> found
;
1314 for (TrackViewList::const_iterator iter
= tracklist
.begin(); iter
!= tracklist
.end(); ++iter
) {
1316 if ((*iter
)->hidden()) {
1320 (*iter
)->get_selectables (start
, end
, top
, bot
, found
);
1323 if (found
.empty()) {
1327 if (preserve_if_selected
&& op
!= Selection::Toggle
) {
1328 list
<Selectable
*>::iterator i
= found
.begin();
1329 while (i
!= found
.end() && (*i
)->get_selected()) {
1333 if (i
== found
.end()) {
1338 begin_reversible_command (_("select all within"));
1340 case Selection::Add
:
1341 selection
->add (found
);
1343 case Selection::Toggle
:
1344 selection
->toggle (found
);
1346 case Selection::Set
:
1347 selection
->set (found
);
1349 case Selection::Extend
:
1350 /* not defined yet */
1354 commit_reversible_command ();
1358 Editor::set_selection_from_region ()
1360 if (selection
->regions
.empty()) {
1364 selection
->set (selection
->regions
.start(), selection
->regions
.end_frame());
1365 if (!Profile
->get_sae()) {
1366 set_mouse_mode (Editing::MouseRange
, false);
1371 Editor::set_selection_from_punch()
1375 if ((location
= _session
->locations()->auto_punch_location()) == 0) {
1379 set_selection_from_range (*location
);
1383 Editor::set_selection_from_loop()
1387 if ((location
= _session
->locations()->auto_loop_location()) == 0) {
1390 set_selection_from_range (*location
);
1394 Editor::set_selection_from_range (Location
& loc
)
1396 begin_reversible_command (_("set selection from range"));
1397 selection
->set (loc
.start(), loc
.end());
1398 commit_reversible_command ();
1400 if (!Profile
->get_sae()) {
1401 set_mouse_mode (Editing::MouseRange
, false);
1406 Editor::select_all_selectables_using_time_selection ()
1408 list
<Selectable
*> touched
;
1410 if (selection
->time
.empty()) {
1414 framepos_t start
= selection
->time
[clicked_selection
].start
;
1415 framepos_t end
= selection
->time
[clicked_selection
].end
;
1417 if (end
- start
< 1) {
1423 if (selection
->tracks
.empty()) {
1426 ts
= &selection
->tracks
;
1429 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1430 if ((*iter
)->hidden()) {
1433 (*iter
)->get_selectables (start
, end
- 1, 0, DBL_MAX
, touched
);
1436 begin_reversible_command (_("select all from range"));
1437 selection
->set (touched
);
1438 commit_reversible_command ();
1443 Editor::select_all_selectables_using_punch()
1445 Location
* location
= _session
->locations()->auto_punch_location();
1446 list
<Selectable
*> touched
;
1448 if (location
== 0 || (location
->end() - location
->start() <= 1)) {
1455 if (selection
->tracks
.empty()) {
1458 ts
= &selection
->tracks
;
1461 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1462 if ((*iter
)->hidden()) {
1465 (*iter
)->get_selectables (location
->start(), location
->end() - 1, 0, DBL_MAX
, touched
);
1467 begin_reversible_command (_("select all from punch"));
1468 selection
->set (touched
);
1469 commit_reversible_command ();
1474 Editor::select_all_selectables_using_loop()
1476 Location
* location
= _session
->locations()->auto_loop_location();
1477 list
<Selectable
*> touched
;
1479 if (location
== 0 || (location
->end() - location
->start() <= 1)) {
1486 if (selection
->tracks
.empty()) {
1489 ts
= &selection
->tracks
;
1492 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1493 if ((*iter
)->hidden()) {
1496 (*iter
)->get_selectables (location
->start(), location
->end() - 1, 0, DBL_MAX
, touched
);
1498 begin_reversible_command (_("select all from loop"));
1499 selection
->set (touched
);
1500 commit_reversible_command ();
1505 Editor::select_all_selectables_using_cursor (EditorCursor
*cursor
, bool after
)
1509 list
<Selectable
*> touched
;
1512 begin_reversible_command (_("select all after cursor"));
1513 start
= cursor
->current_frame
;
1514 end
= _session
->current_end_frame();
1516 if (cursor
->current_frame
> 0) {
1517 begin_reversible_command (_("select all before cursor"));
1519 end
= cursor
->current_frame
- 1;
1528 if (selection
->tracks
.empty()) {
1531 ts
= &selection
->tracks
;
1534 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1535 if ((*iter
)->hidden()) {
1538 (*iter
)->get_selectables (start
, end
, 0, DBL_MAX
, touched
);
1540 selection
->set (touched
);
1541 commit_reversible_command ();
1545 Editor::select_all_selectables_using_edit (bool after
)
1549 list
<Selectable
*> touched
;
1552 begin_reversible_command (_("select all after edit"));
1553 start
= get_preferred_edit_position();
1554 end
= _session
->current_end_frame();
1556 if ((end
= get_preferred_edit_position()) > 1) {
1557 begin_reversible_command (_("select all before edit"));
1568 if (selection
->tracks
.empty()) {
1571 ts
= &selection
->tracks
;
1574 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1575 if ((*iter
)->hidden()) {
1578 (*iter
)->get_selectables (start
, end
, 0, DBL_MAX
, touched
);
1580 selection
->set (touched
);
1581 commit_reversible_command ();
1585 Editor::select_all_selectables_between (bool /*within*/)
1589 list
<Selectable
*> touched
;
1591 if (!get_edit_op_range (start
, end
)) {
1597 if (selection
->tracks
.empty()) {
1600 ts
= &selection
->tracks
;
1603 for (TrackViewList::iterator iter
= ts
->begin(); iter
!= ts
->end(); ++iter
) {
1604 if ((*iter
)->hidden()) {
1607 (*iter
)->get_selectables (start
, end
, 0, DBL_MAX
, touched
);
1610 selection
->set (touched
);
1614 Editor::select_range_between ()
1619 if (mouse_mode
== MouseRange
&& !selection
->time
.empty()) {
1620 selection
->clear_time ();
1623 if (!get_edit_op_range (start
, end
)) {
1627 set_mouse_mode (MouseRange
);
1628 selection
->set (start
, end
);
1632 Editor::get_edit_op_range (framepos_t
& start
, framepos_t
& end
) const
1637 /* in range mode, use any existing selection */
1639 if (mouse_mode
== MouseRange
&& !selection
->time
.empty()) {
1640 /* we know that these are ordered */
1641 start
= selection
->time
.start();
1642 end
= selection
->time
.end_frame();
1646 if (!mouse_frame (m
, ignored
)) {
1647 /* mouse is not in a canvas, try playhead+selected marker.
1648 this is probably most true when using menus.
1651 if (selection
->markers
.empty()) {
1655 start
= selection
->markers
.front()->position();
1656 end
= _session
->audible_frame();
1660 switch (_edit_point
) {
1661 case EditAtPlayhead
:
1662 if (selection
->markers
.empty()) {
1663 /* use mouse + playhead */
1665 end
= _session
->audible_frame();
1667 /* use playhead + selected marker */
1668 start
= _session
->audible_frame();
1669 end
= selection
->markers
.front()->position();
1674 /* use mouse + selected marker */
1675 if (selection
->markers
.empty()) {
1677 end
= _session
->audible_frame();
1679 start
= selection
->markers
.front()->position();
1684 case EditAtSelectedMarker
:
1685 /* use mouse + selected marker */
1686 if (selection
->markers
.empty()) {
1688 MessageDialog
win (_("No edit range defined"),
1693 win
.set_secondary_text (
1694 _("the edit point is Selected Marker\nbut there is no selected marker."));
1697 win
.set_default_response (RESPONSE_CLOSE
);
1698 win
.set_position (Gtk::WIN_POS_MOUSE
);
1703 return false; // NO RANGE
1705 start
= selection
->markers
.front()->position();
1719 /* turn range into one delimited by start...end,
1729 Editor::deselect_all ()
1731 selection
->clear ();
1735 Editor::select_range_around_region (RegionView
* rv
)
1739 selection
->set (&rv
->get_time_axis_view());
1741 selection
->time
.clear ();
1742 boost::shared_ptr
<Region
> r
= rv
->region ();
1743 return selection
->set (r
->position(), r
->position() + r
->length());