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"
30 #include "ardour/audioplaylist.h"
32 #include "control_protocol/control_protocol.h"
36 #include "audio_time_axis.h"
37 #include "audio_region_view.h"
38 #include "audio_streamview.h"
39 #include "automation_line.h"
40 #include "control_point.h"
41 #include "editor_regions.h"
42 #include "editor_cursors.h"
43 #include "midi_region_view.h"
48 using namespace ARDOUR;
52 using namespace Gtkmm2ext;
53 using namespace Editing;
55 struct TrackViewByPositionSorter
57 bool operator() (const TimeAxisView* a, const TimeAxisView *b) {
58 return a->y_position() < b->y_position();
63 Editor::extend_selection_to_track (TimeAxisView& view)
65 if (selection->selected (&view)) {
66 /* already selected, do nothing */
70 if (selection->tracks.empty()) {
72 if (!selection->selected (&view)) {
73 selection->set (&view);
80 /* something is already selected, so figure out which range of things to add */
82 TrackViewList to_be_added;
83 TrackViewList sorted = track_views;
84 TrackViewByPositionSorter cmp;
85 bool passed_clicked = false;
90 if (!selection->selected (&view)) {
91 to_be_added.push_back (&view);
94 /* figure out if we should go forward or backwards */
96 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
99 passed_clicked = true;
102 if (selection->selected (*i)) {
103 if (passed_clicked) {
112 passed_clicked = false;
116 for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) {
119 passed_clicked = true;
123 if (passed_clicked) {
124 if ((*i)->hidden()) {
127 if (selection->selected (*i)) {
129 } else if (!(*i)->hidden()) {
130 to_be_added.push_back (*i);
137 for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) {
140 passed_clicked = true;
144 if (passed_clicked) {
146 if ((*r)->hidden()) {
150 if (selection->selected (*r)) {
152 } else if (!(*r)->hidden()) {
153 to_be_added.push_back (*r);
159 if (!to_be_added.empty()) {
160 selection->add (to_be_added);
168 Editor::select_all_tracks ()
170 TrackViewList visible_views;
171 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
172 if ((*i)->marked_for_display()) {
173 visible_views.push_back (*i);
176 selection->set (visible_views);
179 /** Select clicked_axisview, unless there are no currently selected
180 * tracks, in which case nothing will happen unless `force' is true.
183 Editor::set_selected_track_as_side_effect (Selection::Operation op)
185 if (!clicked_axisview) {
189 if (!clicked_routeview) {
193 bool had_tracks = !selection->tracks.empty();
194 RouteGroup* group = clicked_routeview->route()->route_group();
195 RouteGroup& arg (_session->all_route_group());
198 case Selection::Toggle:
199 if (selection->selected (clicked_axisview)) {
200 if (arg.is_select() && arg.is_active()) {
201 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
202 selection->remove(*i);
204 } else if (group && group->is_active()) {
205 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
206 if ((*i)->route_group() == group)
207 selection->remove(*i);
210 selection->remove (clicked_axisview);
213 if (arg.is_select() && arg.is_active()) {
214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
217 } else if (group && group->is_active()) {
218 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
219 if ( (*i)->route_group() == group)
223 selection->add (clicked_axisview);
229 if (!had_tracks && arg.is_select() && arg.is_active()) {
230 /* nothing was selected already, and all group is active etc. so use
233 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
236 } else if (group && group->is_active()) {
237 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
238 if ((*i)->route_group() == group)
242 selection->add (clicked_axisview);
248 if (!had_tracks && arg.is_select() && arg.is_active()) {
249 /* nothing was selected already, and all group is active etc. so use
252 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
255 } else if (group && group->is_active()) {
256 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end (); ++i) {
257 if ((*i)->route_group() == group)
261 selection->set (clicked_axisview);
265 case Selection::Extend:
272 Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove)
275 case Selection::Toggle:
276 if (selection->selected (&view)) {
278 selection->remove (&view);
281 selection->add (&view);
286 if (!selection->selected (&view)) {
287 selection->add (&view);
292 selection->set (&view);
295 case Selection::Extend:
296 extend_selection_to_track (view);
302 Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool no_remove)
304 if (!clicked_routeview) {
312 set_selected_track (*clicked_routeview, op, no_remove);
316 Editor::set_selected_control_point_from_click (bool press, Selection::Operation op)
318 if (!clicked_control_point) {
328 selection->set (clicked_control_point);
331 selection->add (clicked_control_point);
333 case Selection::Toggle:
334 selection->toggle (clicked_control_point);
336 case Selection::Extend:
345 Editor::get_onscreen_tracks (TrackViewList& tvl)
347 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
348 if ((*i)->y_position() < _canvas_height) {
354 /** Call a slot for a given `basis' track and also for any track that is in the same
355 * active route group with a particular set of properties.
357 * @param sl Slot to call.
358 * @param basis Basis track.
359 * @param prop Properties that active edit groups must share to be included in the map.
363 Editor::mapover_tracks (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
365 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
367 if (route_basis == 0) {
371 set<RouteTimeAxisView*> tracks;
372 tracks.insert (route_basis);
374 RouteGroup* group = route_basis->route()->route_group();
376 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
378 /* the basis is a member of an active route group, with the appropriate
379 properties; find other members */
381 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
382 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
383 if (v && v->route()->route_group() == group) {
390 uint32_t const sz = tracks.size ();
392 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
397 /** Call a slot for a given `basis' track and also for any track that is in the same
398 * active route group with a particular set of properties.
400 * @param sl Slot to call.
401 * @param basis Basis track.
402 * @param prop Properties that active edit groups must share to be included in the map.
406 Editor::mapover_tracks_with_unique_playlists (sigc::slot<void, RouteTimeAxisView&, uint32_t> sl, TimeAxisView* basis, PBD::PropertyID prop) const
408 RouteTimeAxisView* route_basis = dynamic_cast<RouteTimeAxisView*> (basis);
409 set<boost::shared_ptr<Playlist> > playlists;
411 if (route_basis == 0) {
415 set<RouteTimeAxisView*> tracks;
416 tracks.insert (route_basis);
418 RouteGroup* group = route_basis->route()->route_group(); // could be null, not a problem
420 if (group && group->enabled_property(prop) && group->enabled_property (Properties::active.property_id) ) {
422 /* the basis is a member of an active route group, with the appropriate
423 properties; find other members */
425 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
426 RouteTimeAxisView* v = dynamic_cast<RouteTimeAxisView*> (*i);
428 if (v && v->route()->route_group() == group) {
430 boost::shared_ptr<Track> t = v->track();
432 if (playlists.insert (t->playlist()).second) {
433 /* haven't seen this playlist yet */
437 /* not actually a "Track", but a timeaxis view that
438 we should mapover anyway.
447 uint32_t const sz = tracks.size ();
449 for (set<RouteTimeAxisView*>::iterator i = tracks.begin(); i != tracks.end(); ++i) {
455 Editor::mapped_get_equivalent_regions (RouteTimeAxisView& tv, uint32_t, RegionView * basis, vector<RegionView*>* all_equivs) const
457 boost::shared_ptr<Playlist> pl;
458 vector<boost::shared_ptr<Region> > results;
460 boost::shared_ptr<Track> tr;
462 if ((tr = tv.track()) == 0) {
467 if (&tv == &basis->get_time_axis_view()) {
468 /* looking in same track as the original */
472 if ((pl = tr->playlist()) != 0) {
473 pl->get_equivalent_regions (basis->region(), results);
476 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
477 if ((marv = tv.view()->find_view (*ir)) != 0) {
478 all_equivs->push_back (marv);
484 Editor::get_equivalent_regions (RegionView* basis, vector<RegionView*>& equivalent_regions, PBD::PropertyID property) const
486 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);
488 /* add clicked regionview since we skipped all other regions in the same track as the one it was in */
490 equivalent_regions.push_back (basis);
494 Editor::get_equivalent_regions (RegionSelection & basis, PBD::PropertyID prop) const
496 RegionSelection equivalent;
498 for (RegionSelection::const_iterator i = basis.begin(); i != basis.end(); ++i) {
500 vector<RegionView*> eq;
502 mapover_tracks_with_unique_playlists (
503 sigc::bind (sigc::mem_fun (*this, &Editor::mapped_get_equivalent_regions), *i, &eq),
504 &(*i)->get_time_axis_view(), prop);
506 for (vector<RegionView*>::iterator j = eq.begin(); j != eq.end(); ++j) {
517 Editor::get_regionview_count_from_region_list (boost::shared_ptr<Region> region)
519 int region_count = 0;
521 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
523 RouteTimeAxisView* tatv;
525 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
527 boost::shared_ptr<Playlist> pl;
528 vector<boost::shared_ptr<Region> > results;
530 boost::shared_ptr<Track> tr;
532 if ((tr = tatv->track()) == 0) {
537 if ((pl = (tr->playlist())) != 0) {
538 pl->get_region_list_equivalent_regions (region, results);
541 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
542 if ((marv = tatv->view()->find_view (*ir)) != 0) {
555 Editor::set_selected_regionview_from_click (bool press, Selection::Operation op)
557 vector<RegionView*> all_equivalent_regions;
560 if (!clicked_regionview || !clicked_routeview) {
565 button_release_can_deselect = false;
568 if (op == Selection::Toggle || op == Selection::Set) {
571 case Selection::Toggle:
572 if (selection->selected (clicked_regionview)) {
575 /* whatever was clicked was selected already; do nothing here but allow
576 the button release to deselect it
579 button_release_can_deselect = true;
582 if (button_release_can_deselect) {
584 /* just remove this one region, but only on a permitted button release */
586 selection->remove (clicked_regionview);
589 /* no more deselect action on button release till a new press
590 finds an already selected object.
593 button_release_can_deselect = false;
601 if (selection->selected (clicked_routeview)) {
602 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
604 all_equivalent_regions.push_back (clicked_regionview);
607 /* add all the equivalent regions, but only on button press */
609 if (!all_equivalent_regions.empty()) {
613 selection->add (all_equivalent_regions);
619 if (!selection->selected (clicked_regionview)) {
620 get_equivalent_regions (clicked_regionview, all_equivalent_regions, ARDOUR::Properties::select.property_id);
621 selection->set (all_equivalent_regions);
624 /* no commit necessary: clicked on an already selected region */
634 } else if (op == Selection::Extend) {
636 list<Selectable*> results;
637 framepos_t last_frame;
638 framepos_t first_frame;
639 bool same_track = false;
641 /* 1. find the last selected regionview in the track that was clicked in */
644 first_frame = max_framepos;
646 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
647 if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) {
649 if ((*x)->region()->last_frame() > last_frame) {
650 last_frame = (*x)->region()->last_frame();
653 if ((*x)->region()->first_frame() < first_frame) {
654 first_frame = (*x)->region()->first_frame();
663 /* 2. figure out the boundaries for our search for new objects */
665 switch (clicked_regionview->region()->coverage (first_frame, last_frame)) {
666 case Evoral::OverlapNone:
667 if (last_frame < clicked_regionview->region()->first_frame()) {
668 first_frame = last_frame;
669 last_frame = clicked_regionview->region()->last_frame();
671 last_frame = first_frame;
672 first_frame = clicked_regionview->region()->first_frame();
676 case Evoral::OverlapExternal:
677 if (last_frame < clicked_regionview->region()->first_frame()) {
678 first_frame = last_frame;
679 last_frame = clicked_regionview->region()->last_frame();
681 last_frame = first_frame;
682 first_frame = clicked_regionview->region()->first_frame();
686 case Evoral::OverlapInternal:
687 if (last_frame < clicked_regionview->region()->first_frame()) {
688 first_frame = last_frame;
689 last_frame = clicked_regionview->region()->last_frame();
691 last_frame = first_frame;
692 first_frame = clicked_regionview->region()->first_frame();
696 case Evoral::OverlapStart:
697 case Evoral::OverlapEnd:
698 /* nothing to do except add clicked region to selection, since it
699 overlaps with the existing selection in this track.
706 /* click in a track that has no regions selected, so extend vertically
707 to pick out all regions that are defined by the existing selection
712 first_frame = clicked_regionview->region()->position();
713 last_frame = clicked_regionview->region()->last_frame();
715 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
716 if ((*i)->region()->position() < first_frame) {
717 first_frame = (*i)->region()->position();
719 if ((*i)->region()->last_frame() + 1 > last_frame) {
720 last_frame = (*i)->region()->last_frame();
725 /* 2. find all the tracks we should select in */
727 set<RouteTimeAxisView*> relevant_tracks;
729 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
730 RouteTimeAxisView* r = dynamic_cast<RouteTimeAxisView*> (*i);
732 relevant_tracks.insert (r);
736 set<RouteTimeAxisView*> already_in_selection;
738 if (relevant_tracks.empty()) {
740 /* no tracks selected .. thus .. if the
741 regionview we're in isn't selected
742 (i.e. we're about to extend to it), then
743 find all tracks between the this one and
747 if (!selection->selected (clicked_regionview)) {
749 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&clicked_regionview->get_time_axis_view());
753 /* add this track to the ones we will search */
755 relevant_tracks.insert (rtv);
757 /* find the track closest to this one that
758 already a selected region.
761 RouteTimeAxisView* closest = 0;
762 int distance = INT_MAX;
763 int key = rtv->route()->order_key ("editor");
765 for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) {
767 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(&(*x)->get_time_axis_view());
769 if (artv && artv != rtv) {
771 pair<set<RouteTimeAxisView*>::iterator,bool> result;
773 result = already_in_selection.insert (artv);
776 /* newly added to already_in_selection */
778 int d = artv->route()->order_key ("editor");
782 if (abs (d) < distance) {
792 /* now add all tracks between that one and this one */
794 int okey = closest->route()->order_key ("editor");
800 for (TrackViewList::iterator x = track_views.begin(); x != track_views.end(); ++x) {
801 RouteTimeAxisView* artv = dynamic_cast<RouteTimeAxisView*>(*x);
802 if (artv && artv != rtv) {
804 int k = artv->route()->order_key ("editor");
806 if (k >= okey && k <= key) {
808 /* in range but don't add it if
809 it already has tracks selected.
810 this avoids odd selection
811 behaviour that feels wrong.
814 if (find (already_in_selection.begin(),
815 already_in_selection.end(),
816 artv) == already_in_selection.end()) {
818 relevant_tracks.insert (artv);
828 /* 3. find all selectable objects (regionviews in this case) between that one and the end of the
829 one that was clicked.
832 for (set<RouteTimeAxisView*>::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) {
833 (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results);
836 /* 4. convert to a vector of regions */
838 vector<RegionView*> regions;
840 for (list<Selectable*>::iterator x = results.begin(); x != results.end(); ++x) {
843 if ((arv = dynamic_cast<RegionView*>(*x)) != 0) {
844 regions.push_back (arv);
848 if (!regions.empty()) {
849 selection->add (regions);
860 Editor::set_selected_regionview_from_region_list (boost::shared_ptr<Region> region, Selection::Operation op)
862 vector<RegionView*> all_equivalent_regions;
864 get_regions_corresponding_to (region, all_equivalent_regions);
866 if (all_equivalent_regions.empty()) {
870 begin_reversible_command (_("set selected regions"));
873 case Selection::Toggle:
874 /* XXX this is not correct */
875 selection->toggle (all_equivalent_regions);
878 selection->set (all_equivalent_regions);
880 case Selection::Extend:
881 selection->add (all_equivalent_regions);
884 selection->add (all_equivalent_regions);
888 commit_reversible_command () ;
892 Editor::set_selected_regionview_from_map_event (GdkEventAny* /*ev*/, StreamView* sv, boost::weak_ptr<Region> weak_r)
895 boost::shared_ptr<Region> r (weak_r.lock());
901 if ((rv = sv->find_view (r)) == 0) {
905 /* don't reset the selection if its something other than
906 a single other region.
909 if (selection->regions.size() > 1) {
913 begin_reversible_command (_("set selected regions"));
917 commit_reversible_command () ;
923 Editor::track_selection_changed ()
925 switch (selection->tracks.size()) {
929 set_selected_mixer_strip (*(selection->tracks.front()));
933 RouteNotificationListPtr routes (new RouteNotificationList);
935 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
937 bool yn = (find (selection->tracks.begin(), selection->tracks.end(), *i) != selection->tracks.end());
939 (*i)->set_selected (yn);
941 TimeAxisView::Children c = (*i)->get_child_list ();
942 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
943 (*j)->set_selected (find (selection->tracks.begin(), selection->tracks.end(), j->get()) != selection->tracks.end());
947 ((mouse_mode == MouseRange) ||
948 ((mouse_mode == MouseObject) && (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT)))) {
949 (*i)->reshow_selection (selection->time);
951 (*i)->hide_selection ();
956 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*i);
958 routes->push_back (rtav->route());
963 ActionManager::set_sensitive (ActionManager::track_selection_sensitive_actions, !selection->tracks.empty());
965 /* notify control protocols */
967 ControlProtocol::TrackSelectionChanged (routes);
971 Editor::time_selection_changed ()
973 if (Profile->get_sae()) {
977 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
978 (*i)->hide_selection ();
981 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
982 (*i)->show_selection (selection->time);
985 if (selection->time.empty()) {
986 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false);
988 ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true);
991 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->time.empty()) {
992 _session->request_locate (selection->time.start());
996 /** Set all region actions to have a given sensitivity */
998 Editor::sensitize_all_region_actions (bool s)
1000 Glib::ListHandle<Glib::RefPtr<Action> > all = _region_actions->get_actions ();
1002 for (Glib::ListHandle<Glib::RefPtr<Action> >::iterator i = all.begin(); i != all.end(); ++i) {
1003 (*i)->set_sensitive (s);
1006 _all_region_actions_sensitized = s;
1009 /** Sensitize region-based actions based on the selection ONLY, ignoring the entered_regionview.
1010 * This method should be called just before displaying a Region menu. When a Region menu is not
1011 * currently being shown, all region actions are sensitized so that hotkey-triggered actions
1012 * on entered_regionviews work without having to check sensitivity every time the selection or
1013 * entered_regionview changes.
1015 * This method also sets up toggle action state as appropriate.
1018 Editor::sensitize_the_right_region_actions ()
1020 if ((mouse_mode == MouseRange) || (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) {
1021 sensitize_all_region_actions (false);
1022 if (!selection->time.empty()) {
1023 _region_actions->get_action("split-region")->set_sensitive (true);
1027 } else if (mouse_mode != MouseObject) {
1028 sensitize_all_region_actions (false);
1032 /* We get here if we are in Object mode */
1034 RegionSelection rs = get_regions_from_selection_and_entered ();
1035 sensitize_all_region_actions (!rs.empty ());
1037 _ignore_region_action = true;
1039 /* Look through the regions that are selected and make notes about what we have got */
1041 bool have_audio = false;
1042 bool have_multichannel_audio = false;
1043 bool have_midi = false;
1044 bool have_locked = false;
1045 bool have_unlocked = false;
1046 bool have_position_lock_style_audio = false;
1047 bool have_position_lock_style_music = false;
1048 bool have_muted = false;
1049 bool have_unmuted = false;
1050 bool have_opaque = false;
1051 bool have_non_opaque = false;
1052 bool have_not_at_natural_position = false;
1053 bool have_envelope_active = false;
1054 bool have_envelope_inactive = false;
1055 bool have_non_unity_scale_amplitude = false;
1056 bool have_compound_regions = false;
1057 bool have_inactive_fade_in = false;
1058 bool have_inactive_fade_out = false;
1059 bool have_active_fade_in = false;
1060 bool have_active_fade_out = false;
1062 for (list<RegionView*>::const_iterator i = rs.begin(); i != rs.end(); ++i) {
1064 boost::shared_ptr<Region> r = (*i)->region ();
1065 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
1069 if (ar->n_channels() > 1) {
1070 have_multichannel_audio = true;
1074 if (boost::dynamic_pointer_cast<MidiRegion> (r)) {
1078 if (r->is_compound()) {
1079 have_compound_regions = true;
1085 have_unlocked = true;
1088 if (r->position_lock_style() == MusicTime) {
1089 have_position_lock_style_music = true;
1091 have_position_lock_style_audio = true;
1097 have_unmuted = true;
1103 have_non_opaque = true;
1106 if (!r->at_natural_position()) {
1107 have_not_at_natural_position = true;
1111 if (ar->envelope_active()) {
1112 have_envelope_active = true;
1114 have_envelope_inactive = true;
1117 if (ar->scale_amplitude() != 1) {
1118 have_non_unity_scale_amplitude = true;
1121 if (ar->fade_in_active ()) {
1122 have_active_fade_in = true;
1124 have_inactive_fade_in = true;
1127 if (ar->fade_out_active ()) {
1128 have_active_fade_out = true;
1130 have_inactive_fade_out = true;
1135 if (rs.size() > 1) {
1136 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1137 _region_actions->get_action("show-region-properties")->set_sensitive (false);
1138 _region_actions->get_action("rename-region")->set_sensitive (false);
1140 _region_actions->get_action("combine-regions")->set_sensitive (true);
1142 _region_actions->get_action("combine-regions")->set_sensitive (false);
1144 } else if (rs.size() == 1) {
1145 _region_actions->get_action("add-range-markers-from-region")->set_sensitive (false);
1146 _region_actions->get_action("close-region-gaps")->set_sensitive (false);
1147 _region_actions->get_action("combine-regions")->set_sensitive (false);
1150 if (!have_multichannel_audio) {
1151 _region_actions->get_action("split-multichannel-region")->set_sensitive (false);
1155 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (false);
1156 _region_actions->get_action("show-region-list-editor")->set_sensitive (false);
1157 _region_actions->get_action("quantize-region")->set_sensitive (false);
1158 _region_actions->get_action("fork-region")->set_sensitive (false);
1159 _region_actions->get_action("insert-patch-change-context")->set_sensitive (false);
1160 _region_actions->get_action("insert-patch-change")->set_sensitive (false);
1161 _region_actions->get_action("transpose-region")->set_sensitive (false);
1163 editor_menu_actions->get_action("RegionMenuMIDI")->set_sensitive (true);
1164 /* others were already marked sensitive */
1167 if (_edit_point == EditAtMouse) {
1168 _region_actions->get_action("set-region-sync-position")->set_sensitive (false);
1169 _region_actions->get_action("trim-front")->set_sensitive (false);
1170 _region_actions->get_action("trim-back")->set_sensitive (false);
1171 _region_actions->get_action("split-region")->set_sensitive (false);
1172 _region_actions->get_action("place-transient")->set_sensitive (false);
1175 if (have_compound_regions) {
1176 _region_actions->get_action("uncombine-regions")->set_sensitive (true);
1178 _region_actions->get_action("uncombine-regions")->set_sensitive (false);
1183 if (have_envelope_active && !have_envelope_inactive) {
1184 Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_active ();
1185 } else if (have_envelope_active && have_envelope_inactive) {
1186 // Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-gain-envelope-active"))->set_inconsistent ();
1191 _region_actions->get_action("analyze-region")->set_sensitive (false);
1192 _region_actions->get_action("reset-region-gain-envelopes")->set_sensitive (false);
1193 _region_actions->get_action("toggle-region-gain-envelope-active")->set_sensitive (false);
1194 _region_actions->get_action("pitch-shift-region")->set_sensitive (false);
1198 if (!have_non_unity_scale_amplitude || !have_audio) {
1199 _region_actions->get_action("reset-region-scale-amplitude")->set_sensitive (false);
1202 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock"));
1203 a->set_active (have_locked && !have_unlocked);
1204 if (have_locked && have_unlocked) {
1205 // a->set_inconsistent ();
1208 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
1209 a->set_active (have_position_lock_style_music && !have_position_lock_style_audio);
1211 if (have_position_lock_style_music && have_position_lock_style_audio) {
1212 // a->set_inconsistent ();
1215 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-mute"));
1216 a->set_active (have_muted && !have_unmuted);
1217 if (have_muted && have_unmuted) {
1218 // a->set_inconsistent ();
1221 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-opaque-region"));
1222 a->set_active (have_opaque && !have_non_opaque);
1223 if (have_opaque && have_non_opaque) {
1224 // a->set_inconsistent ();
1227 if (!have_not_at_natural_position) {
1228 _region_actions->get_action("naturalize-region")->set_sensitive (false);
1231 /* XXX: should also check that there is a track of the appropriate type for the selected region */
1232 if (_edit_point == EditAtMouse || _regions->get_single_selection() == 0 || selection->tracks.empty()) {
1233 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (false);
1235 _region_actions->get_action("insert-region-from-region-list")->set_sensitive (true);
1238 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-in"));
1239 a->set_active (have_active_fade_in && !have_inactive_fade_in);
1240 if (have_active_fade_in && have_inactive_fade_in) {
1241 // a->set_inconsistent ();
1244 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fade-out"));
1245 a->set_active (have_active_fade_out && !have_inactive_fade_out);
1247 if (have_active_fade_out && have_inactive_fade_out) {
1248 // a->set_inconsistent ();
1251 bool const have_active_fade = have_active_fade_in || have_active_fade_out;
1252 bool const have_inactive_fade = have_inactive_fade_in || have_inactive_fade_out;
1254 a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-fades"));
1255 a->set_active (have_active_fade && !have_inactive_fade);
1257 if (have_active_fade && have_inactive_fade) {
1258 // a->set_inconsistent ();
1261 _ignore_region_action = false;
1263 _all_region_actions_sensitized = false;
1268 Editor::region_selection_changed ()
1270 _regions->block_change_connection (true);
1271 editor_regions_selection_changed_connection.block(true);
1273 if (_region_selection_change_updates_region_list) {
1274 _regions->unselect_all ();
1277 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1278 (*i)->set_selected_regionviews (selection->regions);
1281 if (_region_selection_change_updates_region_list) {
1282 _regions->set_selected (selection->regions);
1285 _regions->block_change_connection (false);
1286 editor_regions_selection_changed_connection.block(false);
1288 if (!_all_region_actions_sensitized) {
1289 /* This selection change might have changed what region actions
1290 are allowed, so sensitize them all in case a key is pressed.
1292 sensitize_all_region_actions (true);
1295 if (_session && Config->get_always_play_range() && !_session->transport_rolling() && !selection->regions.empty()) {
1296 _session->request_locate (selection->regions.start());
1301 Editor::point_selection_changed ()
1303 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1304 (*i)->set_selected_points (selection->points);
1309 Editor::select_all_in_track (Selection::Operation op)
1311 list<Selectable *> touched;
1313 if (!clicked_routeview) {
1317 clicked_routeview->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1320 case Selection::Toggle:
1321 selection->add (touched);
1323 case Selection::Set:
1324 selection->set (touched);
1326 case Selection::Extend:
1327 /* meaningless, because we're selecting everything */
1329 case Selection::Add:
1330 selection->add (touched);
1336 Editor::select_all_internal_edit (Selection::Operation)
1338 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1339 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1341 mrv->select_all_notes ();
1347 Editor::select_all (Selection::Operation op)
1349 list<Selectable *> touched;
1351 if (_internal_editing) {
1352 select_all_internal_edit (op);
1356 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1357 if ((*iter)->hidden()) {
1360 (*iter)->get_selectables (0, max_framepos, 0, DBL_MAX, touched);
1362 begin_reversible_command (_("select all"));
1364 case Selection::Add:
1365 selection->add (touched);
1367 case Selection::Toggle:
1368 selection->add (touched);
1370 case Selection::Set:
1371 selection->set (touched);
1373 case Selection::Extend:
1374 /* meaningless, because we're selecting everything */
1377 commit_reversible_command ();
1381 Editor::invert_selection_in_track ()
1383 list<Selectable *> touched;
1385 if (!clicked_routeview) {
1389 clicked_routeview->get_inverted_selectables (*selection, touched);
1390 selection->set (touched);
1394 Editor::invert_selection ()
1396 list<Selectable *> touched;
1398 if (_internal_editing) {
1399 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1402 mrv->invert_selection ();
1408 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
1409 if ((*iter)->hidden()) {
1412 (*iter)->get_inverted_selectables (*selection, touched);
1415 selection->set (touched);
1418 /** @param start Start time in session frames.
1419 * @param end End time in session frames.
1420 * @param top Top (lower) y limit in trackview coordinates (ie 0 at the top of the track view)
1421 * @param bottom Bottom (higher) y limit in trackview coordinates (ie 0 at the top of the track view)
1422 * @param preserve_if_selected true to leave the current selection alone if we're adding to the selection and all of the selectables
1423 * within the region are already selected.
1426 Editor::select_all_within (framepos_t start, framepos_t end, double top, double bot, const TrackViewList& tracklist, Selection::Operation op, bool preserve_if_selected)
1428 list<Selectable*> found;
1430 for (TrackViewList::const_iterator iter = tracklist.begin(); iter != tracklist.end(); ++iter) {
1432 if ((*iter)->hidden()) {
1436 (*iter)->get_selectables (start, end, top, bot, found);
1439 if (found.empty()) {
1443 if (preserve_if_selected && op != Selection::Toggle) {
1444 list<Selectable*>::iterator i = found.begin();
1445 while (i != found.end() && (*i)->get_selected()) {
1449 if (i == found.end()) {
1454 begin_reversible_command (_("select all within"));
1456 case Selection::Add:
1457 selection->add (found);
1459 case Selection::Toggle:
1460 selection->toggle (found);
1462 case Selection::Set:
1463 selection->set (found);
1465 case Selection::Extend:
1466 /* not defined yet */
1470 commit_reversible_command ();
1474 Editor::set_selection_from_region ()
1476 if (selection->regions.empty()) {
1480 selection->set (selection->regions.start(), selection->regions.end_frame());
1481 if (!Profile->get_sae()) {
1482 set_mouse_mode (Editing::MouseRange, false);
1487 Editor::set_selection_from_punch()
1491 if ((location = _session->locations()->auto_punch_location()) == 0) {
1495 set_selection_from_range (*location);
1499 Editor::set_selection_from_loop()
1503 if ((location = _session->locations()->auto_loop_location()) == 0) {
1506 set_selection_from_range (*location);
1510 Editor::set_selection_from_range (Location& loc)
1512 begin_reversible_command (_("set selection from range"));
1513 selection->set (loc.start(), loc.end());
1514 commit_reversible_command ();
1516 if (!Profile->get_sae()) {
1517 set_mouse_mode (Editing::MouseRange, false);
1522 Editor::select_all_selectables_using_time_selection ()
1524 list<Selectable *> touched;
1526 if (selection->time.empty()) {
1530 framepos_t start = selection->time[clicked_selection].start;
1531 framepos_t end = selection->time[clicked_selection].end;
1533 if (end - start < 1) {
1539 if (selection->tracks.empty()) {
1542 ts = &selection->tracks;
1545 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1546 if ((*iter)->hidden()) {
1549 (*iter)->get_selectables (start, end - 1, 0, DBL_MAX, touched);
1552 begin_reversible_command (_("select all from range"));
1553 selection->set (touched);
1554 commit_reversible_command ();
1559 Editor::select_all_selectables_using_punch()
1561 Location* location = _session->locations()->auto_punch_location();
1562 list<Selectable *> touched;
1564 if (location == 0 || (location->end() - location->start() <= 1)) {
1571 if (selection->tracks.empty()) {
1574 ts = &selection->tracks;
1577 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1578 if ((*iter)->hidden()) {
1581 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1583 begin_reversible_command (_("select all from punch"));
1584 selection->set (touched);
1585 commit_reversible_command ();
1590 Editor::select_all_selectables_using_loop()
1592 Location* location = _session->locations()->auto_loop_location();
1593 list<Selectable *> touched;
1595 if (location == 0 || (location->end() - location->start() <= 1)) {
1602 if (selection->tracks.empty()) {
1605 ts = &selection->tracks;
1608 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1609 if ((*iter)->hidden()) {
1612 (*iter)->get_selectables (location->start(), location->end() - 1, 0, DBL_MAX, touched);
1614 begin_reversible_command (_("select all from loop"));
1615 selection->set (touched);
1616 commit_reversible_command ();
1621 Editor::select_all_selectables_using_cursor (EditorCursor *cursor, bool after)
1625 list<Selectable *> touched;
1628 start = cursor->current_frame;
1629 end = _session->current_end_frame();
1631 if (cursor->current_frame > 0) {
1633 end = cursor->current_frame - 1;
1639 if (_internal_editing) {
1640 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1641 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1643 mrv->select_range (start, end);
1650 begin_reversible_command (_("select all after cursor"));
1652 begin_reversible_command (_("select all before cursor"));
1657 if (selection->tracks.empty()) {
1660 ts = &selection->tracks;
1663 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1664 if ((*iter)->hidden()) {
1667 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1669 selection->set (touched);
1670 commit_reversible_command ();
1674 Editor::select_all_selectables_using_edit (bool after)
1678 list<Selectable *> touched;
1681 start = get_preferred_edit_position();
1682 end = _session->current_end_frame();
1684 if ((end = get_preferred_edit_position()) > 1) {
1692 if (_internal_editing) {
1693 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1694 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1695 mrv->select_range (start, end);
1701 begin_reversible_command (_("select all after edit"));
1703 begin_reversible_command (_("select all before edit"));
1708 if (selection->tracks.empty()) {
1711 ts = &selection->tracks;
1714 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1715 if ((*iter)->hidden()) {
1718 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1720 selection->set (touched);
1721 commit_reversible_command ();
1725 Editor::select_all_selectables_between (bool /*within*/)
1729 list<Selectable *> touched;
1731 if (!get_edit_op_range (start, end)) {
1735 if (_internal_editing) {
1736 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
1737 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
1738 mrv->select_range (start, end);
1745 if (selection->tracks.empty()) {
1748 ts = &selection->tracks;
1751 for (TrackViewList::iterator iter = ts->begin(); iter != ts->end(); ++iter) {
1752 if ((*iter)->hidden()) {
1755 (*iter)->get_selectables (start, end, 0, DBL_MAX, touched);
1758 selection->set (touched);
1762 Editor::select_range_between ()
1767 if (mouse_mode == MouseRange && !selection->time.empty()) {
1768 selection->clear_time ();
1771 if (!get_edit_op_range (start, end)) {
1775 set_mouse_mode (MouseRange);
1776 selection->set (start, end);
1780 Editor::get_edit_op_range (framepos_t& start, framepos_t& end) const
1785 /* in range mode, use any existing selection */
1787 if (mouse_mode == MouseRange && !selection->time.empty()) {
1788 /* we know that these are ordered */
1789 start = selection->time.start();
1790 end = selection->time.end_frame();
1794 if (!mouse_frame (m, ignored)) {
1795 /* mouse is not in a canvas, try playhead+selected marker.
1796 this is probably most true when using menus.
1799 if (selection->markers.empty()) {
1803 start = selection->markers.front()->position();
1804 end = _session->audible_frame();
1808 switch (_edit_point) {
1809 case EditAtPlayhead:
1810 if (selection->markers.empty()) {
1811 /* use mouse + playhead */
1813 end = _session->audible_frame();
1815 /* use playhead + selected marker */
1816 start = _session->audible_frame();
1817 end = selection->markers.front()->position();
1822 /* use mouse + selected marker */
1823 if (selection->markers.empty()) {
1825 end = _session->audible_frame();
1827 start = selection->markers.front()->position();
1832 case EditAtSelectedMarker:
1833 /* use mouse + selected marker */
1834 if (selection->markers.empty()) {
1836 MessageDialog win (_("No edit range defined"),
1841 win.set_secondary_text (
1842 _("the edit point is Selected Marker\nbut there is no selected marker."));
1845 win.set_default_response (RESPONSE_CLOSE);
1846 win.set_position (Gtk::WIN_POS_MOUSE);
1851 return false; // NO RANGE
1853 start = selection->markers.front()->position();
1867 /* turn range into one delimited by start...end,
1877 Editor::deselect_all ()
1879 selection->clear ();
1883 Editor::select_range (framepos_t s, framepos_t e)
1885 selection->add (clicked_axisview);
1886 selection->time.clear ();
1887 return selection->set (s, e);