2
Copyright (C) 2000-2006 Paul Davis
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU General Public License as published by
6
the Free Software Foundation; either version 2 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU General Public License for more details.
14
You should have received a copy of the GNU General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23
#include "pbd/stacktrace.h"
25
#include "ardour/midi_region.h"
26
#include "ardour/playlist.h"
27
#include "ardour/profile.h"
28
#include "ardour/route_group.h"
29
#include "ardour/session.h"
31
#include "control_protocol/control_protocol.h"
35
#include "audio_time_axis.h"
36
#include "audio_region_view.h"
37
#include "audio_streamview.h"
38
#include "automation_line.h"
39
#include "control_point.h"
40
#include "editor_regions.h"
41
#include "editor_cursors.h"
42
#include "midi_region_view.h"
47
using namespace ARDOUR;
51
using namespace Gtkmm2ext;
52
using namespace Editing;
54
struct TrackViewByPositionSorter
56
bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
57
return a->y_position() < b->y_position();
62
Editor::extend_selection_to_track (TimeAxisView& view)
64
if (selection->selected (&view)) {
65
/* already selected, do nothing */
69
if (selection->tracks.empty()) {
71
if (!selection->selected (&view)) {
72
selection->set (&view);
79
/* something is already selected, so figure out which range of things to add */
81
TrackViewList to_be_added;
82
TrackViewList sorted = track_views;
83
TrackViewByPositionSorter cmp;
84
bool passed_clicked = false;
89
if (!selection->selected (&view)) {
90
to_be_added.push_back (&view);
93
/* figure out if we should go forward or backwards */
95
for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
98
passed_clicked = true;
101
if (selection->selected (*i)) {
102
if (passed_clicked) {
111
passed_clicked = false;
115
for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
118
passed_clicked = true;
122
if (passed_clicked) {
123
if ((*i)->hidden()) {
126
if (selection->selected (*i)) {
128
} else if (!(*i)->hidden()) {
129
to_be_added.push_back (*i);
136
for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
139
passed_clicked = true;
143
if (passed_clicked) {
145
if ((*r)->hidden()) {
149
if (selection->selected (*r)) {
151
} else if (!(*r)->hidden()) {
152
to_be_added.push_back (*r);
158
if (!to_be_added.empty()) {
159
selection->add (to_be_added);
167
Editor::select_all_tracks ()
169
TrackViewList visible_views;
170
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
171
if ((*i)->marked_for_display()) {
172
visible_views.push_back (*i);
175
selection->set (visible_views);
178
/** Select clicked_axisview, unless there are no currently selected
179
* tracks, in which case nothing will happen unless `force' is true.
182
Editor::set_selected_track_as_side_effect (Selection::Operation op)
184
if (!clicked_axisview) {
188
if (!clicked_routeview) {
192
bool had_tracks = !selection->tracks.empty();
193
RouteGroup* group = clicked_routeview->route()->route_group();
194
RouteGroup& arg (_session->all_route_group());
197
case Selection::Toggle:
198
if (selection->selected (clicked_axisview)) {
199
if (arg.is_select() && arg.is_active()) {
200
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
201
selection->remove(*i);
203
} else if (group && group->is_active()) {
204
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
205
if ((*i)->route_group() == group)
206
selection->remove(*i);
209
selection->remove (clicked_axisview);
212
if (arg.is_select() && arg.is_active()) {
213
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
216
} else if (group && group->is_active()) {
217
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
218
if ( (*i)->route_group() == group)
222
selection->add (clicked_axisview);
228
if (!had_tracks && arg.is_select() && arg.is_active()) {
229
/* nothing was selected already, and all group is active etc. so use
232
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
235
} else if (group && group->is_active()) {
236
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
237
if ((*i)->route_group() == group)
241
selection->add (clicked_axisview);
247
if (!had_tracks && arg.is_select() && arg.is_active()) {
248
/* nothing was selected already, and all group is active etc. so use
251
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
254
} else if (group && group->is_active()) {
255
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
256
if ((*i)->route_group() == group)
260
selection->set (clicked_axisview);
264
case Selection::Extend:
271
Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
274
case Selection::Toggle:
275
if (selection->selected (&view)) {
277
selection->remove (&view);
280
selection->add (&view);
285
if (!selection->selected (&view)) {
286
selection->add (&view);
291
selection->set (&view);
294
case Selection::Extend:
295
extend_selection_to_track (view);
301
Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
303
if (!clicked_routeview) {
311
set_selected_track (*clicked_routeview, op, no_remove);
315
Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
317
if (!clicked_control_point) {
324
selection->set (clicked_control_point);
329
selection->add (clicked_control_point);
332
case Selection::Toggle:
333
/* This is a bit of a hack; if we Primary-Click-Drag a control
334
point (for push drag) we want the point we clicked on to be
335
selected, otherwise we end up confusingly dragging an
336
unselected point. So here we ensure that the point is selected
337
after the press, and if we subsequently get a release (meaning no
338
drag occurred) we set things up so that the toggle has happened.
340
if (press && !selection->selected (clicked_control_point)) {
341
/* This is the button press, and the control point is not selected; make it so,
342
in case this press leads to a drag. Also note that having done this, we don't
343
need to toggle again on release.
345
selection->toggle (clicked_control_point);
346
_control_point_toggled_on_press = true;
347
} else if (!press && !_control_point_toggled_on_press) {
348
/* This is the release, and the point wasn't toggled on the press, so do it now */
349
selection->toggle (clicked_control_point);
352
_control_point_toggled_on_press = false;
355
case Selection::Extend:
364
Editor::get_onscreen_tracks (TrackViewList& tvl)
366
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
367
if ((*i)->y_position() < _canvas_height) {
373
/** Call a slot for a given `basis' track and also for any track that is in the same
374
* active route group with a particular set of properties.
376
* @param sl Slot to call.
377
* @param basis Basis track.
378
* @param prop Properties that active edit groups must share to be included in the map.
382
Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
384
RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
386
if (route_basis == 0) {
390
set<RouteTimeAxisView*> tracks;
391
tracks.insert (route_basis);
393
RouteGroup* group = route_basis->route()->route_group();
395
if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
397
/* the basis is a member of an active route group, with the appropriate
398
properties; find other members */
400
for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
401
RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
402
if (v && v->route()->route_group() == group) {
409
uint32_t const sz = tracks.size ();
411
for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
416
/** Call a slot for a given `basis' track and also for any track that is in the same
417
* active route group with a particular set of properties.
419
* @param sl Slot to call.
420
* @param basis Basis track.
421
* @param prop Properties that active edit groups must share to be included in the map.
425
Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
427
RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
428
set<boost::shared_ptr<Playlist> > playlists;
430
if (route_basis == 0) {
434
set<RouteTimeAxisView*> tracks;
435
tracks.insert (route_basis);
437
RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
439
if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
441
/* the basis is a member of an active route group, with the appropriate
442
properties; find other members */
444
for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
445
RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
447
if (v && v->route()->route_group() == group) {
449
boost::shared_ptr<Track> t = v->track();
451
if (playlists.insert (t->playlist()).second) {
452
/* haven't seen this playlist yet */
456
/* not actually a "Track", but a timeaxis view that
457
we should mapover anyway.
466
uint32_t const sz = tracks.size ();
468
for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
474
Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
476
boost::shared_ptr<Playlist> pl;
477
vector<boost::shared_ptr<Region> > results;
479
boost::shared_ptr<Track> tr;
481
if ((tr = tv.track()) == 0) {
486
if (&tv == &basis->get_time_axis_view()) {
487
/* looking in same track as the original */
491
if ((pl = tr->playlist()) != 0) {
492
pl->get_equivalent_regions (basis->region(), results);
495
for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
496
if ((marv = tv.view()->find_view (*ir)) != 0) {
497
all_equivs->push_back (marv);
503
Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
505
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);
507
/* add clicked regionview since we skipped all other regions in the same track as the one it was in */
509
equivalent_regions.push_back (basis);
513
Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
515
RegionSelection equivalent;
517
for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
519
vector<RegionView*> eq;
521
mapover_tracks_with_unique_playlists (
522
sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
523
&(*i)->get_time_axis_view(), prop);
525
for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
536
Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
538
int region_count = 0;
540
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
542
RouteTimeAxisView* tatv;
544
if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
546
boost::shared_ptr<Playlist> pl;
547
vector<boost::shared_ptr<Region> > results;
549
boost::shared_ptr<Track> tr;
551
if ((tr = tatv->track()) == 0) {
556
if ((pl = (tr->playlist())) != 0) {
557
pl->get_region_list_equivalent_regions (region, results);
560
for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
561
if ((marv = tatv->view()->find_view (*ir)) != 0) {
574
Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
576
vector<RegionView*> all_equivalent_regions;
579
if (!clicked_regionview || !clicked_routeview) {
584
button_release_can_deselect = false;
587
if (op == Selection::Toggle || op == Selection::Set) {
590
case Selection::Toggle:
591
if (selection->selected (clicked_regionview)) {
594
/* whatever was clicked was selected already; do nothing here but allow
595
the button release to deselect it
598
button_release_can_deselect = true;
601
if (button_release_can_deselect) {
603
/* just remove this one region, but only on a permitted button release */
605
selection->remove (clicked_regionview);
608
/* no more deselect action on button release till a new press
609
finds an already selected object.
612
button_release_can_deselect = false;
620
if (selection->selected (clicked_routeview)) {
621
get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
623
all_equivalent_regions.push_back (clicked_regionview);
626
/* add all the equivalent regions, but only on button press */
628
if (!all_equivalent_regions.empty()) {
632
selection->add (all_equivalent_regions);
638
if (!selection->selected (clicked_regionview)) {
639
get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
640
selection->set (all_equivalent_regions);
643
/* no commit necessary: clicked on an already selected region */
653
} else if (op == Selection::Extend) {
655
list<Selectable*> results;
656
framepos_t last_frame;
657
framepos_t first_frame;
658
bool same_track = false;
660
/* 1. find the last selected regionview in the track that was clicked in */
663
first_frame = max_framepos;
665
for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
666
if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
668
if ((*x)->region()->last_frame() > last_frame) {
669
last_frame = (*x)->region()->last_frame();
672
if ((*x)->region()->first_frame() < first_frame) {
673
first_frame = (*x)->region()->first_frame();
682
/* 2. figure out the boundaries for our search for new objects */
684
switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
685
case Evoral::OverlapNone:
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 Evoral::OverlapExternal:
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();
705
case Evoral::OverlapInternal:
706
if (last_frame < clicked_regionview->region()->first_frame()) {
707
first_frame = last_frame;
708
last_frame = clicked_regionview->region()->last_frame();
710
last_frame = first_frame;
711
first_frame = clicked_regionview->region()->first_frame();
715
case Evoral::OverlapStart:
716
case Evoral::OverlapEnd:
717
/* nothing to do except add clicked region to selection, since it
718
overlaps with the existing selection in this track.
725
/* click in a track that has no regions selected, so extend vertically
726
to pick out all regions that are defined by the existing selection
731
first_frame = clicked_regionview->region()->position();
732
last_frame = clicked_regionview->region()->last_frame();
734
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
735
if ((*i)->region()->position() < first_frame) {
736
first_frame = (*i)->region()->position();
738
if ((*i)->region()->last_frame() + 1 > last_frame) {
739
last_frame = (*i)->region()->last_frame();
744
/* 2. find all the tracks we should select in */
746
set<RouteTimeAxisView*> relevant_tracks;
748
for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
749
RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
751
relevant_tracks.insert (r);
755
set<RouteTimeAxisView*> already_in_selection;
757
if (relevant_tracks.empty()) {
759
/* no tracks selected .. thus .. if the
760
regionview we're in isn't selected
761
(i.e. we're about to extend to it), then
762
find all tracks between the this one and
766
if (!selection->selected (clicked_regionview)) {
768
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
772
/* add this track to the ones we will search */
774
relevant_tracks.insert (rtv);
776
/* find the track closest to this one that
777
already a selected region.
780
RouteTimeAxisView* closest = 0;
781
int distance = INT_MAX;
782
int key = rtv->route()->order_key (EditorSort);
784
for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
786
RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
788
if (artv && artv != rtv) {
790
pair<set<RouteTimeAxisView*>::iterator,bool> result;
792
result = already_in_selection.insert (artv);
795
/* newly added to already_in_selection */
797
int d = artv->route()->order_key (EditorSort);
801
if (abs (d) < distance) {
811
/* now add all tracks between that one and this one */
813
int okey = closest->route()->order_key (EditorSort);
819
for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
820
RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
821
if (artv && artv != rtv) {
823
int k = artv->route()->order_key (EditorSort);
825
if (k >= okey && k <= key) {
827
/* in range but don't add it if
828
it already has tracks selected.
829
this avoids odd selection
830
behaviour that feels wrong.
833
if (find (already_in_selection.begin(),
834
already_in_selection.end(),
835
artv) == already_in_selection.end()) {
837
relevant_tracks.insert (artv);
847
/* 3. find all selectable objects (regionviews in this case) between that one and the end of the
848
one that was clicked.
851
for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
852
(*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
855
/* 4. convert to a vector of regions */
857
vector<RegionView*> regions;
859
for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
862
if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
863
regions.push_back (arv);
867
if (!regions.empty()) {
868
selection->add (regions);
879
Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
881
vector<RegionView*> all_equivalent_regions;
883
get_regions_corresponding_to (region, all_equivalent_regions, region->whole_file());
885
if (all_equivalent_regions.empty()) {
889
begin_reversible_command (_("set selected regions"));
892
case Selection::Toggle:
893
/* XXX this is not correct */
894
selection->toggle (all_equivalent_regions);
897
selection->set (all_equivalent_regions);
899
case Selection::Extend:
900
selection->add (all_equivalent_regions);
903
selection->add (all_equivalent_regions);
907
commit_reversible_command () ;
911
Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
914
boost::shared_ptr<Region> r (weak_r.lock());
920
if ((rv = sv->find_view (r)) == 0) {
924
/* don't reset the selection if its something other than
925
a single other region.
928
if (selection->regions.size() > 1) {
932
begin_reversible_command (_("set selected regions"));
936
commit_reversible_command () ;
942
Editor::track_selection_changed ()
944
switch (selection->tracks.size()) {
948
set_selected_mixer_strip (*(selection->tracks.front()));
952
RouteNotificationListPtr routes (new RouteNotificationList);
954
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
956
bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
958
(*i)->set_selected (yn);
960
TimeAxisView::Children c = (*i)->get_child_list ();
961
for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
962
(*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
966
(*i)->reshow_selection (selection->time);
968
(*i)->hide_selection ();
973
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
975
routes->push_back (rtav->route());
980
ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
982
/* notify control protocols */
984
ControlProtocol::TrackSelectionChanged (routes);
988
Editor::time_selection_changed ()
990
if (Profile->get_sae()) {
994
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
995
(*i)->hide_selection ();
998
for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
999
(*i)->show_selection (selection->time);
1002
if (selection->time.empty()) {
1003
ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
1005
ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
1008
if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
1009
_session->request_locate (selection->time.start());
1013
/** Set all region actions to have a given sensitivity */
1015
Editor::sensitize_all_region_actions (bool s)
1017
Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1019
for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1020
(*i)->set_sensitive (s);
1023
_all_region_actions_sensitized = s;
1026
/** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1027
* This method should be called just before displaying a Region menu. When a Region menu is not
1028
* currently being shown, all region actions are sensitized so that hotkey-triggered actions
1029
* on entered_regionviews work without having to check sensitivity every time the selection or
1030
* entered_regionview changes.
1032
* This method also sets up toggle action state as appropriate.
1035
Editor::sensitize_the_right_region_actions ()
1038
RegionSelection rs = get_regions_from_selection_and_entered ();
1039
sensitize_all_region_actions (!rs.empty ());
1041
_ignore_region_action = true;
1043
/* Look through the regions that are selected and make notes about what we have got */
1045
bool have_audio = false;
1046
bool have_multichannel_audio = false;
1047
bool have_midi = false;
1048
bool have_locked = false;
1049
bool have_unlocked = false;
1050
bool have_video_locked = false;
1051
bool have_video_unlocked = false;
1052
bool have_position_lock_style_audio = false;
1053
bool have_position_lock_style_music = false;
1054
bool have_muted = false;
1055
bool have_unmuted = false;
1056
bool have_opaque = false;
1057
bool have_non_opaque = false;
1058
bool have_not_at_natural_position = false;
1059
bool have_envelope_active = false;
1060
bool have_envelope_inactive = false;
1061
bool have_non_unity_scale_amplitude = false;
1062
bool have_compound_regions = false;
1063
bool have_inactive_fade_in = false;
1064
bool have_inactive_fade_out = false;
1065
bool have_active_fade_in = false;
1066
bool have_active_fade_out = false;
1068
for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1070
boost::shared_ptr<Region> r = (*i)->region ();
1071
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1075
if (ar->n_channels() > 1) {
1076
have_multichannel_audio = true;
1080
if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1084
if (r->is_compound()) {
1085
have_compound_regions = true;
1091
have_unlocked = true;
1094
if (r->video_locked()) {
1095
have_video_locked = true;
1097
have_video_unlocked = true;
1100
if (r->position_lock_style() == MusicTime) {
1101
have_position_lock_style_music = true;
1103
have_position_lock_style_audio = true;
1109
have_unmuted = true;
1115
have_non_opaque = true;
1118
if (!r->at_natural_position()) {
1119
have_not_at_natural_position = true;
1123
if (ar->envelope_active()) {
1124
have_envelope_active = true;
1126
have_envelope_inactive = true;
1129
if (ar->scale_amplitude() != 1) {
1130
have_non_unity_scale_amplitude = true;
1133
if (ar->fade_in_active ()) {
1134
have_active_fade_in = true;
1136
have_inactive_fade_in = true;
1139
if (ar->fade_out_active ()) {
1140
have_active_fade_out = true;
1142
have_inactive_fade_out = true;
1147
if (rs.size() > 1) {
1148
_region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1149
_region_actions->get_action("show-region-properties")->set_sensitive (false);
1150
_region_actions->get_action("rename-region")->set_sensitive (false);
1152
/* XXX need to check whether there is than 1 per
1153
playlist, because otherwise this makes no sense.
1155
_region_actions->get_action("combine-regions")->set_sensitive (true);
1157
_region_actions->get_action("combine-regions")->set_sensitive (false);
1159
} else if (rs.size() == 1) {
1160
_region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1161
_region_actions->get_action("close-region-gaps")->set_sensitive (false);
1162
_region_actions->get_action("combine-regions")->set_sensitive (false);
1165
if (!have_multichannel_audio) {
1166
_region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1170
editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1171
_region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1172
_region_actions->get_action("quantize-region")->set_sensitive (false);
1173
_region_actions->get_action("fork-region")->set_sensitive (false);
1174
_region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1175
_region_actions->get_action("insert-patch-change")->set_sensitive (false);
1176
_region_actions->get_action("transpose-region")->set_sensitive (false);
1178
editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1179
/* others were already marked sensitive */
1182
if (_edit_point == EditAtMouse) {
1183
_region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1184
_region_actions->get_action("trim-front")->set_sensitive (false);
1185
_region_actions->get_action("trim-back")->set_sensitive (false);
1186
_region_actions->get_action("split-region")->set_sensitive (false);
1187
_region_actions->get_action("place-transient")->set_sensitive (false);
1190
if (have_compound_regions) {
1191
_region_actions->get_action("uncombine-regions")->set_sensitive (true);
1193
_region_actions->get_action("uncombine-regions")->set_sensitive (false);
1198
if (have_envelope_active && !have_envelope_inactive) {
1199
Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1200
} else if (have_envelope_active && have_envelope_inactive) {
1201
// Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1206
_region_actions->get_action("analyze-region")->set_sensitive (false);
1207
_region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1208
_region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1209
_region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1213
if (!have_non_unity_scale_amplitude || !have_audio) {
1214
_region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1217
Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1218
a->set_active (have_locked && !have_unlocked);
1219
if (have_locked && have_unlocked) {
1220
// a->set_inconsistent ();
1223
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-video-lock"));
1224
a->set_active (have_video_locked && !have_video_unlocked);
1225
if (have_video_locked && have_video_unlocked) {
1226
// a->set_inconsistent ();
1229
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1230
a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1232
if (have_position_lock_style_music && have_position_lock_style_audio) {
1233
// a->set_inconsistent ();
1236
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1237
a->set_active (have_muted && !have_unmuted);
1238
if (have_muted && have_unmuted) {
1239
// a->set_inconsistent ();
1242
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1243
a->set_active (have_opaque && !have_non_opaque);
1244
if (have_opaque && have_non_opaque) {
1245
// a->set_inconsistent ();
1248
if (!have_not_at_natural_position) {
1249
_region_actions->get_action("naturalize-region")->set_sensitive (false);
1252
/* XXX: should also check that there is a track of the appropriate type for the selected region */
1253
if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1254
_region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1256
_region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1259
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1260
a->set_active (have_active_fade_in && !have_inactive_fade_in);
1261
if (have_active_fade_in && have_inactive_fade_in) {
1262
// a->set_inconsistent ();
1265
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1266
a->set_active (have_active_fade_out && !have_inactive_fade_out);
1268
if (have_active_fade_out && have_inactive_fade_out) {
1269
// a->set_inconsistent ();
1272
bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1273
bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1275
a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1276
a->set_active (have_active_fade && !have_inactive_fade);
1278
if (have_active_fade && have_inactive_fade) {
1279
// a->set_inconsistent ();
1282
_ignore_region_action = false;
1284
_all_region_actions_sensitized = false;
1289
Editor::region_selection_changed ()
1291
_regions->block_change_connection (true);
1292
editor_regions_selection_changed_connection.block(true);
1294
if (_region_selection_change_updates_region_list) {
1295
_regions->unselect_all ();
1298
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1299
(*i)->set_selected_regionviews (selection->regions);
1302
if (_region_selection_change_updates_region_list) {
1303
_regions->set_selected (selection->regions);
1306
_regions->block_change_connection (false);
1307
editor_regions_selection_changed_connection.block(false);
1309
if (!_all_region_actions_sensitized) {
1310
/* This selection change might have changed what region actions
1311
are allowed, so sensitize them all in case a key is pressed.
1313
sensitize_all_region_actions (true);
1316
if (_session && !_session->transport_rolling() && !selection->regions.empty()) {
1317
maybe_locate_with_edit_preroll (selection->regions.start());
1322
Editor::point_selection_changed ()
1324
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1325
(*i)->set_selected_points (selection->points);
1330
Editor::select_all_in_track (Selection::Operation op)
1332
list<Selectable *> touched;
1334
if (!clicked_routeview) {
1338
clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1341
case Selection::Toggle:
1342
selection->add (touched);
1344
case Selection::Set:
1345
selection->set (touched);
1347
case Selection::Extend:
1348
/* meaningless, because we're selecting everything */
1350
case Selection::Add:
1351
selection->add (touched);
1357
Editor::select_all_internal_edit (Selection::Operation)
1359
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1360
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1362
mrv->select_all_notes ();
1368
Editor::select_all (Selection::Operation op)
1370
list<Selectable *> touched;
1374
if (selection->tracks.empty()) {
1375
if (entered_track) {
1376
ts.push_back (entered_track);
1381
ts = selection->tracks;
1384
if (_internal_editing) {
1386
bool midi_selected = false;
1388
for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1389
if ((*iter)->hidden()) {
1393
RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*iter);
1395
if (rtav && rtav->is_midi_track()) {
1396
midi_selected = true;
1401
if (midi_selected) {
1402
select_all_internal_edit (op);
1407
for (TrackViewList::iterator iter = ts.begin(); iter != ts.end(); ++iter) {
1408
if ((*iter)->hidden()) {
1411
(*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1414
begin_reversible_command (_("select all"));
1416
case Selection::Add:
1417
selection->add (touched);
1419
case Selection::Toggle:
1420
selection->add (touched);
1422
case Selection::Set:
1423
selection->set (touched);
1425
case Selection::Extend:
1426
/* meaningless, because we're selecting everything */
1429
commit_reversible_command ();
1433
Editor::invert_selection_in_track ()
1435
list<Selectable *> touched;
1437
if (!clicked_routeview) {
1441
clicked_routeview->get_inverted_selectables (*selection, touched);
1442
selection->set (touched);
1446
Editor::invert_selection ()
1448
list<Selectable *> touched;
1450
if (_internal_editing) {
1451
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1452
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1454
mrv->invert_selection ();
1460
for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1461
if ((*iter)->hidden()) {
1464
(*iter)->get_inverted_selectables (*selection, touched);
1467
selection->set (touched);
1470
/** @param start Start time in session frames.
1471
* @param end End time in session frames.
1472
* @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1473
* @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1474
* @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1475
* within the region are already selected.
1478
Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1480
list<Selectable*> found;
1482
for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1484
if ((*iter)->hidden()) {
1488
(*iter)->get_selectables (start, end, top, bot, found);
1491
if (found.empty()) {
1495
if (preserve_if_selected && op != Selection::Toggle) {
1496
list<Selectable*>::iterator i = found.begin();
1497
while (i != found.end() && (*i)->get_selected()) {
1501
if (i == found.end()) {
1506
begin_reversible_command (_("select all within"));
1508
case Selection::Add:
1509
selection->add (found);
1511
case Selection::Toggle:
1512
selection->toggle (found);
1514
case Selection::Set:
1515
selection->set (found);
1517
case Selection::Extend:
1518
/* not defined yet */
1522
commit_reversible_command ();
1526
Editor::set_selection_from_region ()
1528
if (selection->regions.empty()) {
1532
selection->set (selection->regions.start(), selection->regions.end_frame());
1533
if (!Profile->get_sae()) {
1534
set_mouse_mode (Editing::MouseRange, false);
1539
Editor::set_selection_from_punch()
1543
if ((location = _session->locations()->auto_punch_location()) == 0) {
1547
set_selection_from_range (*location);
1551
Editor::set_selection_from_loop()
1555
if ((location = _session->locations()->auto_loop_location()) == 0) {
1558
set_selection_from_range (*location);
1562
Editor::set_selection_from_range (Location& loc)
1564
begin_reversible_command (_("set selection from range"));
1565
selection->set (loc.start(), loc.end());
1566
commit_reversible_command ();
1568
if (!Profile->get_sae()) {
1569
set_mouse_mode (Editing::MouseRange, false);
1574
Editor::select_all_selectables_using_time_selection ()
1576
list<Selectable *> touched;
1578
if (selection->time.empty()) {
1582
framepos_t start = selection->time[clicked_selection].start;
1583
framepos_t end = selection->time[clicked_selection].end;
1585
if (end - start < 1) {
1591
if (selection->tracks.empty()) {
1594
ts = &selection->tracks;
1597
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1598
if ((*iter)->hidden()) {
1601
(*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1604
begin_reversible_command (_("select all from range"));
1605
selection->set (touched);
1606
commit_reversible_command ();
1611
Editor::select_all_selectables_using_punch()
1613
Location* location = _session->locations()->auto_punch_location();
1614
list<Selectable *> touched;
1616
if (location == 0 || (location->end() - location->start() <= 1)) {
1623
if (selection->tracks.empty()) {
1626
ts = &selection->tracks;
1629
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1630
if ((*iter)->hidden()) {
1633
(*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1635
begin_reversible_command (_("select all from punch"));
1636
selection->set (touched);
1637
commit_reversible_command ();
1642
Editor::select_all_selectables_using_loop()
1644
Location* location = _session->locations()->auto_loop_location();
1645
list<Selectable *> touched;
1647
if (location == 0 || (location->end() - location->start() <= 1)) {
1654
if (selection->tracks.empty()) {
1657
ts = &selection->tracks;
1660
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1661
if ((*iter)->hidden()) {
1664
(*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1666
begin_reversible_command (_("select all from loop"));
1667
selection->set (touched);
1668
commit_reversible_command ();
1673
Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1677
list<Selectable *> touched;
1680
start = cursor->current_frame;
1681
end = _session->current_end_frame();
1683
if (cursor->current_frame > 0) {
1685
end = cursor->current_frame - 1;
1691
if (_internal_editing) {
1692
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1693
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1695
mrv->select_range (start, end);
1702
begin_reversible_command (_("select all after cursor"));
1704
begin_reversible_command (_("select all before cursor"));
1709
if (selection->tracks.empty()) {
1712
ts = &selection->tracks;
1715
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1716
if ((*iter)->hidden()) {
1719
(*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1721
selection->set (touched);
1722
commit_reversible_command ();
1726
Editor::select_all_selectables_using_edit (bool after)
1730
list<Selectable *> touched;
1733
start = get_preferred_edit_position();
1734
end = _session->current_end_frame();
1736
if ((end = get_preferred_edit_position()) > 1) {
1744
if (_internal_editing) {
1745
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1746
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1747
mrv->select_range (start, end);
1753
begin_reversible_command (_("select all after edit"));
1755
begin_reversible_command (_("select all before edit"));
1760
if (selection->tracks.empty()) {
1763
ts = &selection->tracks;
1766
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1767
if ((*iter)->hidden()) {
1770
(*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1772
selection->set (touched);
1773
commit_reversible_command ();
1777
Editor::select_all_selectables_between (bool /*within*/)
1781
list<Selectable *> touched;
1783
if (!get_edit_op_range (start, end)) {
1787
if (_internal_editing) {
1788
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1789
MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1790
mrv->select_range (start, end);
1797
if (selection->tracks.empty()) {
1800
ts = &selection->tracks;
1803
for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1804
if ((*iter)->hidden()) {
1807
(*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1810
selection->set (touched);
1814
Editor::select_range_between ()
1819
if ( !selection->time.empty() ) {
1820
selection->clear_time ();
1823
if (!get_edit_op_range (start, end)) {
1827
set_mouse_mode (MouseRange);
1828
selection->set (start, end);
1832
Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1837
/* if an explicit range exists, use it */
1839
if (!selection->time.empty()) {
1840
/* we know that these are ordered */
1841
start = selection->time.start();
1842
end = selection->time.end_frame();
1846
if (!mouse_frame (m, ignored)) {
1847
/* mouse is not in a canvas, try playhead+selected marker.
1848
this is probably most true when using menus.
1851
if (selection->markers.empty()) {
1855
start = selection->markers.front()->position();
1856
end = _session->audible_frame();
1860
switch (_edit_point) {
1861
case EditAtPlayhead:
1862
if (selection->markers.empty()) {
1863
/* use mouse + playhead */
1865
end = _session->audible_frame();
1867
/* use playhead + selected marker */
1868
start = _session->audible_frame();
1869
end = selection->markers.front()->position();
1874
/* use mouse + selected marker */
1875
if (selection->markers.empty()) {
1877
end = _session->audible_frame();
1879
start = selection->markers.front()->position();
1884
case EditAtSelectedMarker:
1885
/* use mouse + selected marker */
1886
if (selection->markers.empty()) {
1888
MessageDialog win (_("No edit range defined"),
1893
win.set_secondary_text (
1894
_("the edit point is Selected Marker\nbut there is no selected marker."));
1897
win.set_default_response (RESPONSE_CLOSE);
1898
win.set_position (Gtk::WIN_POS_MOUSE);
1903
return false; // NO RANGE
1905
start = selection->markers.front()->position();
1919
/* turn range into one delimited by start...end,
1929
Editor::deselect_all ()
1931
selection->clear ();
1935
Editor::select_range (framepos_t s, framepos_t e)
1937
selection->add (clicked_axisview);
1938
selection->time.clear ();
1939
return selection->set (s, e);