2 Copyright (C) 2000-2004 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.
20 /* Note: public Editor methods are documented in public_editor.h */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/session.h"
62 #include "ardour/session_playlists.h"
63 #include "ardour/strip_silence.h"
64 #include "ardour/transient_detector.h"
65 #include "ardour/transpose.h"
66 #include "ardour/vca_manager.h"
68 #include "canvas/canvas.h"
71 #include "audio_region_view.h"
72 #include "audio_streamview.h"
73 #include "audio_time_axis.h"
74 #include "automation_region_view.h"
75 #include "automation_time_axis.h"
76 #include "control_point.h"
80 #include "editor_cursors.h"
81 #include "editor_drag.h"
82 #include "editor_regions.h"
83 #include "editor_routes.h"
84 #include "gui_thread.h"
85 #include "insert_remove_time_dialog.h"
86 #include "interthread_progress_window.h"
87 #include "item_counts.h"
89 #include "midi_region_view.h"
91 #include "mixer_strip.h"
92 #include "mouse_cursors.h"
93 #include "normalize_dialog.h"
95 #include "paste_context.h"
96 #include "patch_change_dialog.h"
97 #include "quantize_dialog.h"
98 #include "region_gain_line.h"
99 #include "rgb_macros.h"
100 #include "route_time_axis.h"
101 #include "selection.h"
102 #include "selection_templates.h"
103 #include "streamview.h"
104 #include "strip_silence_dialog.h"
105 #include "time_axis_view.h"
107 #include "transpose_dialog.h"
108 #include "transform_dialog.h"
109 #include "ui_config.h"
110 #include "vca_time_axis.h"
112 #include "pbd/i18n.h"
115 using namespace ARDOUR;
118 using namespace Gtkmm2ext;
119 using namespace ArdourWidgets;
120 using namespace Editing;
121 using Gtkmm2ext::Keyboard;
123 /***********************************************************************
125 ***********************************************************************/
128 Editor::undo (uint32_t n)
130 if (_session && _session->actively_recording()) {
131 /* no undo allowed while recording. Session will check also,
132 but we don't even want to get to that.
137 if (_drags->active ()) {
143 if (_session->undo_depth() == 0) {
144 undo_action->set_sensitive(false);
146 redo_action->set_sensitive(true);
147 begin_selection_op_history ();
152 Editor::redo (uint32_t n)
154 if (_session && _session->actively_recording()) {
155 /* no redo allowed while recording. Session will check also,
156 but we don't even want to get to that.
161 if (_drags->active ()) {
167 if (_session->redo_depth() == 0) {
168 redo_action->set_sensitive(false);
170 undo_action->set_sensitive(true);
171 begin_selection_op_history ();
176 Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap_sample)
180 RegionSelection pre_selected_regions = selection->regions;
181 bool working_on_selection = !pre_selected_regions.empty();
183 list<boost::shared_ptr<Playlist> > used_playlists;
184 list<RouteTimeAxisView*> used_trackviews;
186 if (regions.empty()) {
190 begin_reversible_command (_("split"));
192 // if splitting a single region, and snap-to is using
193 // region boundaries, don't pay attention to them
195 if (regions.size() == 1) {
196 // switch (_snap_type) { //ToDo !!!
197 // case SnapToRegionStart:
198 // case SnapToRegionSync:
199 // case SnapToRegionEnd:
202 // if (snap_sample) {
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.sample)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 if (working_on_selection) {
285 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
288 /* There are three classes of regions that we might want selected after
289 splitting selected regions:
290 - regions selected before the split operation, and unaffected by it
291 - newly-created regions before the split
292 - newly-created regions after the split
295 if (rsas & Existing) {
296 // region selections that existed before the split.
297 selection->add (pre_selected_regions);
300 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
301 if ((*ri)->region()->position() < where.sample) {
302 // new regions created before the split
303 if (rsas & NewlyCreatedLeft) {
304 selection->add (*ri);
307 // new regions created after the split
308 if (rsas & NewlyCreatedRight) {
309 selection->add (*ri);
315 commit_reversible_command ();
318 /** Move one extreme of the current range selection. If more than one range is selected,
319 * the start of the earliest range or the end of the latest range is moved.
321 * @param move_end true to move the end of the current range selection, false to move
323 * @param next true to move the extreme to the next region boundary, false to move to
327 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
329 if (selection->time.start() == selection->time.end_sample()) {
333 samplepos_t start = selection->time.start ();
334 samplepos_t end = selection->time.end_sample ();
336 /* the position of the thing we may move */
337 samplepos_t pos = move_end ? end : start;
338 int dir = next ? 1 : -1;
340 /* so we don't find the current region again */
341 if (dir > 0 || pos > 0) {
345 samplepos_t const target = get_region_boundary (pos, dir, true, false);
360 begin_reversible_selection_op (_("alter selection"));
361 selection->set_preserving_all_ranges (start, end);
362 commit_reversible_selection_op ();
366 Editor::nudge_forward_release (GdkEventButton* ev)
368 if (ev->state & Keyboard::PrimaryModifier) {
369 nudge_forward (false, true);
371 nudge_forward (false, false);
377 Editor::nudge_backward_release (GdkEventButton* ev)
379 if (ev->state & Keyboard::PrimaryModifier) {
380 nudge_backward (false, true);
382 nudge_backward (false, false);
389 Editor::nudge_forward (bool next, bool force_playhead)
391 samplepos_t distance;
392 samplepos_t next_distance;
398 RegionSelection rs = get_regions_from_selection_and_entered ();
400 if (!force_playhead && !rs.empty()) {
402 begin_reversible_command (_("nudge regions forward"));
404 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
405 boost::shared_ptr<Region> r ((*i)->region());
407 distance = get_nudge_distance (r->position(), next_distance);
410 distance = next_distance;
414 r->set_position (r->position() + distance);
415 _session->add_command (new StatefulDiffCommand (r));
418 commit_reversible_command ();
421 } else if (!force_playhead && !selection->markers.empty()) {
424 bool in_command = false;
425 const int32_t divisions = get_grid_music_divisions (0);
427 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
429 Location* loc = find_location_from_marker ((*i), is_start);
433 XMLNode& before (loc->get_state());
436 distance = get_nudge_distance (loc->start(), next_distance);
438 distance = next_distance;
440 if (max_samplepos - distance > loc->start() + loc->length()) {
441 loc->set_start (loc->start() + distance, false, true, divisions);
443 loc->set_start (max_samplepos - loc->length(), false, true, divisions);
446 distance = get_nudge_distance (loc->end(), next_distance);
448 distance = next_distance;
450 if (max_samplepos - distance > loc->end()) {
451 loc->set_end (loc->end() + distance, false, true, divisions);
453 loc->set_end (max_samplepos, false, true, divisions);
455 if (loc->is_session_range()) {
456 _session->set_end_is_free (false);
460 begin_reversible_command (_("nudge location forward"));
463 XMLNode& after (loc->get_state());
464 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
469 commit_reversible_command ();
472 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
473 _session->request_locate (playhead_cursor->current_sample () + distance);
478 Editor::nudge_backward (bool next, bool force_playhead)
480 samplepos_t distance;
481 samplepos_t next_distance;
487 RegionSelection rs = get_regions_from_selection_and_entered ();
489 if (!force_playhead && !rs.empty()) {
491 begin_reversible_command (_("nudge regions backward"));
493 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
494 boost::shared_ptr<Region> r ((*i)->region());
496 distance = get_nudge_distance (r->position(), next_distance);
499 distance = next_distance;
504 if (r->position() > distance) {
505 r->set_position (r->position() - distance);
509 _session->add_command (new StatefulDiffCommand (r));
512 commit_reversible_command ();
514 } else if (!force_playhead && !selection->markers.empty()) {
517 bool in_command = false;
519 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
521 Location* loc = find_location_from_marker ((*i), is_start);
525 XMLNode& before (loc->get_state());
528 distance = get_nudge_distance (loc->start(), next_distance);
530 distance = next_distance;
532 if (distance < loc->start()) {
533 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
535 loc->set_start (0, false, true, get_grid_music_divisions(0));
538 distance = get_nudge_distance (loc->end(), next_distance);
541 distance = next_distance;
544 if (distance < loc->end() - loc->length()) {
545 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
547 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
549 if (loc->is_session_range()) {
550 _session->set_end_is_free (false);
554 begin_reversible_command (_("nudge location forward"));
557 XMLNode& after (loc->get_state());
558 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
562 commit_reversible_command ();
567 distance = get_nudge_distance (playhead_cursor->current_sample (), next_distance);
569 if (playhead_cursor->current_sample () > distance) {
570 _session->request_locate (playhead_cursor->current_sample () - distance);
572 _session->goto_start();
578 Editor::nudge_forward_capture_offset ()
580 RegionSelection rs = get_regions_from_selection_and_entered ();
582 if (!_session || rs.empty()) {
586 begin_reversible_command (_("nudge forward"));
588 samplepos_t const distance = _session->worst_output_latency();
590 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
591 boost::shared_ptr<Region> r ((*i)->region());
594 r->set_position (r->position() + distance);
595 _session->add_command(new StatefulDiffCommand (r));
598 commit_reversible_command ();
602 Editor::nudge_backward_capture_offset ()
604 RegionSelection rs = get_regions_from_selection_and_entered ();
606 if (!_session || rs.empty()) {
610 begin_reversible_command (_("nudge backward"));
612 samplepos_t const distance = _session->worst_output_latency();
614 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
615 boost::shared_ptr<Region> r ((*i)->region());
619 if (r->position() > distance) {
620 r->set_position (r->position() - distance);
624 _session->add_command(new StatefulDiffCommand (r));
627 commit_reversible_command ();
630 struct RegionSelectionPositionSorter {
631 bool operator() (RegionView* a, RegionView* b) {
632 return a->region()->position() < b->region()->position();
637 Editor::sequence_regions ()
640 samplepos_t r_end_prev;
648 RegionSelection rs = get_regions_from_selection_and_entered ();
649 rs.sort(RegionSelectionPositionSorter());
653 bool in_command = false;
655 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
656 boost::shared_ptr<Region> r ((*i)->region());
664 if(r->position_locked())
671 r->set_position(r_end_prev);
675 begin_reversible_command (_("sequence regions"));
678 _session->add_command (new StatefulDiffCommand (r));
680 r_end=r->position() + r->length();
686 commit_reversible_command ();
695 Editor::move_to_start ()
697 _session->goto_start ();
701 Editor::move_to_end ()
704 _session->request_locate (_session->current_end_sample());
708 Editor::build_region_boundary_cache ()
711 //ToDo: maybe set a timer so we don't recalutate when lots of changes are coming in
712 //ToDo: maybe somehow defer this until session is fully loaded.
714 if ( !_region_boundary_cache_dirty )
718 vector<RegionPoint> interesting_points;
719 boost::shared_ptr<Region> r;
720 TrackViewList tracks;
723 region_boundary_cache.clear ();
729 bool maybe_first_sample = false;
731 if ( UIConfiguration::instance().get_snap_to_region_start() ) {
732 interesting_points.push_back (Start);
733 maybe_first_sample = true;
736 if ( UIConfiguration::instance().get_snap_to_region_end() ) {
737 interesting_points.push_back (End);
740 if ( UIConfiguration::instance().get_snap_to_region_sync() ) {
741 interesting_points.push_back (SyncPoint);
744 TimeAxisView *ontrack = 0;
747 //in the past, we used the track selection to limit snap. I think this is not desired.
748 //or if it is, it needs to be updated every time the track selection changes (so the snapped-cursor can show it)
749 // if (!selection->tracks.empty()) {
750 // tlist = selection->tracks.filter_to_unique_playlists ();
752 tlist = track_views.filter_to_unique_playlists ();
755 if (maybe_first_sample) {
756 TrackViewList::const_iterator i;
757 for (i = tlist.begin(); i != tlist.end(); ++i) {
758 boost::shared_ptr<Playlist> pl = (*i)->playlist();
759 if (pl && pl->count_regions_at (0)) {
760 region_boundary_cache.push_back (0);
766 while (pos < _session->current_end_sample() && !at_end) {
769 samplepos_t lpos = max_samplepos;
771 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
773 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
774 if (*p == interesting_points.back()) {
777 /* move to next point type */
783 rpos = r->first_sample();
787 rpos = r->last_sample();
791 rpos = r->sync_position ();
802 /* prevent duplicates, but we don't use set<> because we want to be able
806 vector<samplepos_t>::iterator ri;
808 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
814 if (ri == region_boundary_cache.end()) {
815 region_boundary_cache.push_back (rpos);
822 /* finally sort to be sure that the order is correct */
824 sort (region_boundary_cache.begin(), region_boundary_cache.end());
826 _region_boundary_cache_dirty = false;
829 boost::shared_ptr<Region>
830 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
832 TrackViewList::iterator i;
833 samplepos_t closest = max_samplepos;
834 boost::shared_ptr<Region> ret;
835 samplepos_t rpos = 0;
837 samplepos_t track_sample;
839 for (i = tracks.begin(); i != tracks.end(); ++i) {
841 samplecnt_t distance;
842 boost::shared_ptr<Region> r;
844 track_sample = sample;
846 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
852 rpos = r->first_sample ();
856 rpos = r->last_sample ();
860 rpos = r->sync_position ();
865 distance = rpos - sample;
867 distance = sample - rpos;
870 if (distance < closest) {
882 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
884 samplecnt_t distance = max_samplepos;
885 samplepos_t current_nearest = -1;
887 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
888 samplepos_t contender;
891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
897 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
901 d = ::llabs (pos - contender);
904 current_nearest = contender;
909 return current_nearest;
913 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
918 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
920 if (!selection->tracks.empty()) {
922 target = find_next_region_boundary (pos, dir, selection->tracks);
926 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
927 get_onscreen_tracks (tvl);
928 target = find_next_region_boundary (pos, dir, tvl);
930 target = find_next_region_boundary (pos, dir, track_views);
936 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
937 get_onscreen_tracks (tvl);
938 target = find_next_region_boundary (pos, dir, tvl);
940 target = find_next_region_boundary (pos, dir, track_views);
948 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
950 samplepos_t pos = playhead_cursor->current_sample ();
957 // so we don't find the current region again..
958 if (dir > 0 || pos > 0) {
962 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
966 _session->request_locate (target);
970 Editor::cursor_to_next_region_boundary (bool with_selection)
972 cursor_to_region_boundary (with_selection, 1);
976 Editor::cursor_to_previous_region_boundary (bool with_selection)
978 cursor_to_region_boundary (with_selection, -1);
982 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
984 boost::shared_ptr<Region> r;
985 samplepos_t pos = cursor->current_sample ();
991 TimeAxisView *ontrack = 0;
993 // so we don't find the current region again..
997 if (!selection->tracks.empty()) {
999 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1001 } else if (clicked_axisview) {
1004 t.push_back (clicked_axisview);
1006 r = find_next_region (pos, point, dir, t, &ontrack);
1010 r = find_next_region (pos, point, dir, track_views, &ontrack);
1019 pos = r->first_sample ();
1023 pos = r->last_sample ();
1027 pos = r->sync_position ();
1031 if (cursor == playhead_cursor) {
1032 _session->request_locate (pos);
1034 cursor->set_position (pos);
1039 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1041 cursor_to_region_point (cursor, point, 1);
1045 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1047 cursor_to_region_point (cursor, point, -1);
1051 Editor::cursor_to_selection_start (EditorCursor *cursor)
1053 samplepos_t pos = 0;
1055 switch (mouse_mode) {
1057 if (!selection->regions.empty()) {
1058 pos = selection->regions.start();
1063 if (!selection->time.empty()) {
1064 pos = selection->time.start ();
1072 if (cursor == playhead_cursor) {
1073 _session->request_locate (pos);
1075 cursor->set_position (pos);
1080 Editor::cursor_to_selection_end (EditorCursor *cursor)
1082 samplepos_t pos = 0;
1084 switch (mouse_mode) {
1086 if (!selection->regions.empty()) {
1087 pos = selection->regions.end_sample();
1092 if (!selection->time.empty()) {
1093 pos = selection->time.end_sample ();
1101 if (cursor == playhead_cursor) {
1102 _session->request_locate (pos);
1104 cursor->set_position (pos);
1109 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1119 if (selection->markers.empty()) {
1123 if (!mouse_sample (mouse, ignored)) {
1127 add_location_mark (mouse);
1130 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1134 samplepos_t pos = loc->start();
1136 // so we don't find the current region again..
1137 if (dir > 0 || pos > 0) {
1141 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1145 loc->move_to (target, 0);
1149 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1151 selected_marker_to_region_boundary (with_selection, 1);
1155 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1157 selected_marker_to_region_boundary (with_selection, -1);
1161 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1163 boost::shared_ptr<Region> r;
1168 if (!_session || selection->markers.empty()) {
1172 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1176 TimeAxisView *ontrack = 0;
1180 // so we don't find the current region again..
1184 if (!selection->tracks.empty()) {
1186 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1190 r = find_next_region (pos, point, dir, track_views, &ontrack);
1199 pos = r->first_sample ();
1203 pos = r->last_sample ();
1207 pos = r->adjust_to_sync (r->first_sample());
1211 loc->move_to (pos, 0);
1215 Editor::selected_marker_to_next_region_point (RegionPoint point)
1217 selected_marker_to_region_point (point, 1);
1221 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1223 selected_marker_to_region_point (point, -1);
1227 Editor::selected_marker_to_selection_start ()
1229 samplepos_t pos = 0;
1233 if (!_session || selection->markers.empty()) {
1237 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1241 switch (mouse_mode) {
1243 if (!selection->regions.empty()) {
1244 pos = selection->regions.start();
1249 if (!selection->time.empty()) {
1250 pos = selection->time.start ();
1258 loc->move_to (pos, 0);
1262 Editor::selected_marker_to_selection_end ()
1264 samplepos_t pos = 0;
1268 if (!_session || selection->markers.empty()) {
1272 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1276 switch (mouse_mode) {
1278 if (!selection->regions.empty()) {
1279 pos = selection->regions.end_sample();
1284 if (!selection->time.empty()) {
1285 pos = selection->time.end_sample ();
1293 loc->move_to (pos, 0);
1297 Editor::scroll_playhead (bool forward)
1299 samplepos_t pos = playhead_cursor->current_sample ();
1300 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1303 if (pos == max_samplepos) {
1307 if (pos < max_samplepos - delta) {
1310 pos = max_samplepos;
1326 _session->request_locate (pos);
1330 Editor::cursor_align (bool playhead_to_edit)
1336 if (playhead_to_edit) {
1338 if (selection->markers.empty()) {
1342 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1345 const int32_t divisions = get_grid_music_divisions (0);
1346 /* move selected markers to playhead */
1348 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1351 Location* loc = find_location_from_marker (*i, ignored);
1353 if (loc->is_mark()) {
1354 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1356 loc->set (playhead_cursor->current_sample (),
1357 playhead_cursor->current_sample () + loc->length(), true, divisions);
1364 Editor::scroll_backward (float pages)
1366 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1367 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1370 if (_leftmost_sample < cnt) {
1373 sample = _leftmost_sample - cnt;
1376 reset_x_origin (sample);
1380 Editor::scroll_forward (float pages)
1382 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1383 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1386 if (max_samplepos - cnt < _leftmost_sample) {
1387 sample = max_samplepos - cnt;
1389 sample = _leftmost_sample + cnt;
1392 reset_x_origin (sample);
1396 Editor::scroll_tracks_down ()
1398 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1399 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1400 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1403 vertical_adjustment.set_value (vert_value);
1407 Editor::scroll_tracks_up ()
1409 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1413 Editor::scroll_tracks_down_line ()
1415 double vert_value = vertical_adjustment.get_value() + 60;
1417 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1418 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1421 vertical_adjustment.set_value (vert_value);
1425 Editor::scroll_tracks_up_line ()
1427 reset_y_origin (vertical_adjustment.get_value() - 60);
1431 Editor::select_topmost_track ()
1433 const double top_of_trackviews = vertical_adjustment.get_value();
1434 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1435 if ((*t)->hidden()) {
1438 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1440 selection->set (*t);
1447 Editor::scroll_down_one_track (bool skip_child_views)
1449 TrackViewList::reverse_iterator next = track_views.rend();
1450 const double top_of_trackviews = vertical_adjustment.get_value();
1452 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1453 if ((*t)->hidden()) {
1457 /* If this is the upper-most visible trackview, we want to display
1458 * the one above it (next)
1460 * Note that covers_y_position() is recursive and includes child views
1462 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1465 if (skip_child_views) {
1468 /* automation lane (one level, non-recursive)
1470 * - if no automation lane exists -> move to next tack
1471 * - if the first (here: bottom-most) matches -> move to next tack
1472 * - if no y-axis match is found -> the current track is at the top
1473 * -> move to last (here: top-most) automation lane
1475 TimeAxisView::Children kids = (*t)->get_child_list();
1476 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1478 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1479 if ((*ci)->hidden()) {
1483 std::pair<TimeAxisView*,double> dev;
1484 dev = (*ci)->covers_y_position (top_of_trackviews);
1486 /* some automation lane is currently at the top */
1487 if (ci == kids.rbegin()) {
1488 /* first (bottom-most) autmation lane is at the top.
1489 * -> move to next track
1498 if (nkid != kids.rend()) {
1499 ensure_time_axis_view_is_visible (**nkid, true);
1507 /* move to the track below the first one that covers the */
1509 if (next != track_views.rend()) {
1510 ensure_time_axis_view_is_visible (**next, true);
1518 Editor::scroll_up_one_track (bool skip_child_views)
1520 TrackViewList::iterator prev = track_views.end();
1521 double top_of_trackviews = vertical_adjustment.get_value ();
1523 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1525 if ((*t)->hidden()) {
1529 /* find the trackview at the top of the trackview group
1531 * Note that covers_y_position() is recursive and includes child views
1533 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1536 if (skip_child_views) {
1539 /* automation lane (one level, non-recursive)
1541 * - if no automation lane exists -> move to prev tack
1542 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1543 * (actually last automation lane of previous track, see below)
1544 * - if first (top-most) lane is at the top -> move to this track
1545 * - else move up one lane
1547 TimeAxisView::Children kids = (*t)->get_child_list();
1548 TimeAxisView::Children::iterator pkid = kids.end();
1550 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1551 if ((*ci)->hidden()) {
1555 std::pair<TimeAxisView*,double> dev;
1556 dev = (*ci)->covers_y_position (top_of_trackviews);
1558 /* some automation lane is currently at the top */
1559 if (ci == kids.begin()) {
1560 /* first (top-most) autmation lane is at the top.
1561 * jump directly to this track's top
1563 ensure_time_axis_view_is_visible (**t, true);
1566 else if (pkid != kids.end()) {
1567 /* some other automation lane is at the top.
1568 * move up to prev automation lane.
1570 ensure_time_axis_view_is_visible (**pkid, true);
1573 assert(0); // not reached
1584 if (prev != track_views.end()) {
1585 // move to bottom-most automation-lane of the previous track
1586 TimeAxisView::Children kids = (*prev)->get_child_list();
1587 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1588 if (!skip_child_views) {
1589 // find the last visible lane
1590 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1591 if (!(*ci)->hidden()) {
1597 if (pkid != kids.rend()) {
1598 ensure_time_axis_view_is_visible (**pkid, true);
1600 ensure_time_axis_view_is_visible (**prev, true);
1609 Editor::scroll_left_step ()
1611 samplepos_t xdelta = (current_page_samples() / 8);
1613 if (_leftmost_sample > xdelta) {
1614 reset_x_origin (_leftmost_sample - xdelta);
1622 Editor::scroll_right_step ()
1624 samplepos_t xdelta = (current_page_samples() / 8);
1626 if (max_samplepos - xdelta > _leftmost_sample) {
1627 reset_x_origin (_leftmost_sample + xdelta);
1629 reset_x_origin (max_samplepos - current_page_samples());
1634 Editor::scroll_left_half_page ()
1636 samplepos_t xdelta = (current_page_samples() / 2);
1637 if (_leftmost_sample > xdelta) {
1638 reset_x_origin (_leftmost_sample - xdelta);
1645 Editor::scroll_right_half_page ()
1647 samplepos_t xdelta = (current_page_samples() / 2);
1648 if (max_samplepos - xdelta > _leftmost_sample) {
1649 reset_x_origin (_leftmost_sample + xdelta);
1651 reset_x_origin (max_samplepos - current_page_samples());
1658 Editor::tav_zoom_step (bool coarser)
1660 DisplaySuspender ds;
1664 if (selection->tracks.empty()) {
1667 ts = &selection->tracks;
1670 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1671 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1672 tv->step_height (coarser);
1677 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1679 DisplaySuspender ds;
1683 if (selection->tracks.empty() || force_all) {
1686 ts = &selection->tracks;
1689 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1690 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1691 uint32_t h = tv->current_height ();
1696 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1701 tv->set_height (h + 5);
1707 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1709 Editing::ZoomFocus temp_focus = zoom_focus;
1710 zoom_focus = Editing::ZoomFocusMouse;
1711 temporal_zoom_step_scale (zoom_out, scale);
1712 zoom_focus = temp_focus;
1716 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1718 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1722 Editor::temporal_zoom_step (bool zoom_out)
1724 temporal_zoom_step_scale (zoom_out, 2.0);
1728 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1730 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1732 samplecnt_t nspp = samples_per_pixel;
1736 if (nspp == samples_per_pixel) {
1741 if (nspp == samples_per_pixel) {
1746 //zoom-behavior-tweaks
1747 //limit our maximum zoom to the session gui extents value
1748 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1749 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1750 if (nspp > session_extents_pp)
1751 nspp = session_extents_pp;
1753 temporal_zoom (nspp);
1757 Editor::temporal_zoom (samplecnt_t fpp)
1763 samplepos_t current_page = current_page_samples();
1764 samplepos_t current_leftmost = _leftmost_sample;
1765 samplepos_t current_rightmost;
1766 samplepos_t current_center;
1767 samplepos_t new_page_size;
1768 samplepos_t half_page_size;
1769 samplepos_t leftmost_after_zoom = 0;
1771 bool in_track_canvas;
1772 bool use_mouse_sample = true;
1776 if (fpp == samples_per_pixel) {
1780 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1781 // segfaults for lack of memory. If somebody decides this is not high enough I
1782 // believe it can be raisen to higher values but some limit must be in place.
1784 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1785 // all of which is used for the editor track displays. The whole day
1786 // would be 4147200000 samples, so 2592000 samples per pixel.
1788 nfpp = min (fpp, (samplecnt_t) 2592000);
1789 nfpp = max ((samplecnt_t) 1, nfpp);
1791 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1792 half_page_size = new_page_size / 2;
1794 switch (zoom_focus) {
1796 leftmost_after_zoom = current_leftmost;
1799 case ZoomFocusRight:
1800 current_rightmost = _leftmost_sample + current_page;
1801 if (current_rightmost < new_page_size) {
1802 leftmost_after_zoom = 0;
1804 leftmost_after_zoom = current_rightmost - new_page_size;
1808 case ZoomFocusCenter:
1809 current_center = current_leftmost + (current_page/2);
1810 if (current_center < half_page_size) {
1811 leftmost_after_zoom = 0;
1813 leftmost_after_zoom = current_center - half_page_size;
1817 case ZoomFocusPlayhead:
1818 /* centre playhead */
1819 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1822 leftmost_after_zoom = 0;
1823 } else if (l > max_samplepos) {
1824 leftmost_after_zoom = max_samplepos - new_page_size;
1826 leftmost_after_zoom = (samplepos_t) l;
1830 case ZoomFocusMouse:
1831 /* try to keep the mouse over the same point in the display */
1833 if (_drags->active()) {
1834 where = _drags->current_pointer_sample ();
1835 } else if (!mouse_sample (where, in_track_canvas)) {
1836 use_mouse_sample = false;
1839 if (use_mouse_sample) {
1840 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1843 leftmost_after_zoom = 0;
1844 } else if (l > max_samplepos) {
1845 leftmost_after_zoom = max_samplepos - new_page_size;
1847 leftmost_after_zoom = (samplepos_t) l;
1850 /* use playhead instead */
1851 where = playhead_cursor->current_sample ();
1853 if (where < half_page_size) {
1854 leftmost_after_zoom = 0;
1856 leftmost_after_zoom = where - half_page_size;
1862 /* try to keep the edit point in the same place */
1863 where = get_preferred_edit_position ();
1867 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1870 leftmost_after_zoom = 0;
1871 } else if (l > max_samplepos) {
1872 leftmost_after_zoom = max_samplepos - new_page_size;
1874 leftmost_after_zoom = (samplepos_t) l;
1878 /* edit point not defined */
1885 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1887 reposition_and_zoom (leftmost_after_zoom, nfpp);
1891 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1893 /* this func helps make sure we leave a little space
1894 at each end of the editor so that the zoom doesn't fit the region
1895 precisely to the screen.
1898 GdkScreen* screen = gdk_screen_get_default ();
1899 const gint pixwidth = gdk_screen_get_width (screen);
1900 const gint mmwidth = gdk_screen_get_width_mm (screen);
1901 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1902 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1904 const samplepos_t range = end - start;
1905 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1906 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1908 if (start > extra_samples) {
1909 start -= extra_samples;
1914 if (max_samplepos - extra_samples > end) {
1915 end += extra_samples;
1917 end = max_samplepos;
1922 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1924 start = max_samplepos;
1928 //ToDo: if notes are selected, set extents to that selection
1930 //ToDo: if control points are selected, set extents to that selection
1932 if (!selection->regions.empty()) {
1933 RegionSelection rs = get_regions_from_selection_and_entered ();
1935 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1937 if ((*i)->region()->position() < start) {
1938 start = (*i)->region()->position();
1941 if ((*i)->region()->last_sample() + 1 > end) {
1942 end = (*i)->region()->last_sample() + 1;
1946 } else if (!selection->time.empty()) {
1947 start = selection->time.start();
1948 end = selection->time.end_sample();
1950 ret = false; //no selection found
1953 if ((start == 0 && end == 0) || end < start) {
1962 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1964 if (!selection) return;
1966 //ToDo: if notes are selected, zoom to that
1968 //ToDo: if control points are selected, zoom to that
1970 if (axes == Horizontal || axes == Both) {
1972 samplepos_t start, end;
1973 if (get_selection_extents (start, end)) {
1974 calc_extra_zoom_edges (start, end);
1975 temporal_zoom_by_sample (start, end);
1979 if (axes == Vertical || axes == Both) {
1983 //normally, we don't do anything "automatic" to the user's selection.
1984 //but in this case, we will clear the selection after a zoom-to-selection.
1989 Editor::temporal_zoom_session ()
1991 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1994 samplecnt_t start = _session->current_start_sample();
1995 samplecnt_t end = _session->current_end_sample();
1997 if (_session->actively_recording ()) {
1998 samplepos_t cur = playhead_cursor->current_sample ();
2000 /* recording beyond the end marker; zoom out
2001 * by 5 seconds more so that if 'follow
2002 * playhead' is active we don't immediately
2005 end = cur + _session->sample_rate() * 5;
2009 if ((start == 0 && end == 0) || end < start) {
2013 calc_extra_zoom_edges(start, end);
2015 temporal_zoom_by_sample (start, end);
2020 Editor::temporal_zoom_extents ()
2022 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2025 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false); //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
2027 samplecnt_t start = ext.first;
2028 samplecnt_t end = ext.second;
2030 if (_session->actively_recording ()) {
2031 samplepos_t cur = playhead_cursor->current_sample ();
2033 /* recording beyond the end marker; zoom out
2034 * by 5 seconds more so that if 'follow
2035 * playhead' is active we don't immediately
2038 end = cur + _session->sample_rate() * 5;
2042 if ((start == 0 && end == 0) || end < start) {
2046 calc_extra_zoom_edges(start, end);
2048 temporal_zoom_by_sample (start, end);
2053 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2055 if (!_session) return;
2057 if ((start == 0 && end == 0) || end < start) {
2061 samplepos_t range = end - start;
2063 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2065 samplepos_t new_page = range;
2066 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2067 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2069 if (new_leftmost > middle) {
2073 if (new_leftmost < 0) {
2077 reposition_and_zoom (new_leftmost, new_fpp);
2081 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2087 samplecnt_t range_before = sample - _leftmost_sample;
2088 samplecnt_t new_spp;
2091 if (samples_per_pixel <= 1) {
2094 new_spp = samples_per_pixel + (samples_per_pixel/2);
2096 range_before += range_before/2;
2098 if (samples_per_pixel >= 1) {
2099 new_spp = samples_per_pixel - (samples_per_pixel/2);
2101 /* could bail out here since we cannot zoom any finer,
2102 but leave that to the equality test below
2104 new_spp = samples_per_pixel;
2107 range_before -= range_before/2;
2110 if (new_spp == samples_per_pixel) {
2114 /* zoom focus is automatically taken as @param sample when this
2118 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2120 if (new_leftmost > sample) {
2124 if (new_leftmost < 0) {
2128 reposition_and_zoom (new_leftmost, new_spp);
2133 Editor::choose_new_marker_name(string &name) {
2135 if (!UIConfiguration::instance().get_name_new_markers()) {
2136 /* don't prompt user for a new name */
2140 Prompter dialog (true);
2142 dialog.set_prompt (_("New Name:"));
2144 dialog.set_title (_("New Location Marker"));
2146 dialog.set_name ("MarkNameWindow");
2147 dialog.set_size_request (250, -1);
2148 dialog.set_position (Gtk::WIN_POS_MOUSE);
2150 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2151 dialog.set_initial_text (name);
2155 switch (dialog.run ()) {
2156 case RESPONSE_ACCEPT:
2162 dialog.get_result(name);
2169 Editor::add_location_from_selection ()
2173 if (selection->time.empty()) {
2177 if (_session == 0 || clicked_axisview == 0) {
2181 samplepos_t start = selection->time[clicked_selection].start;
2182 samplepos_t end = selection->time[clicked_selection].end;
2184 _session->locations()->next_available_name(rangename,"selection");
2185 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2187 begin_reversible_command (_("add marker"));
2189 XMLNode &before = _session->locations()->get_state();
2190 _session->locations()->add (location, true);
2191 XMLNode &after = _session->locations()->get_state();
2192 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2194 commit_reversible_command ();
2198 Editor::add_location_mark (samplepos_t where)
2202 select_new_marker = true;
2204 _session->locations()->next_available_name(markername,"mark");
2205 if (!choose_new_marker_name(markername)) {
2208 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2209 begin_reversible_command (_("add marker"));
2211 XMLNode &before = _session->locations()->get_state();
2212 _session->locations()->add (location, true);
2213 XMLNode &after = _session->locations()->get_state();
2214 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2216 commit_reversible_command ();
2220 Editor::set_session_start_from_playhead ()
2226 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2227 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2229 XMLNode &before = loc->get_state();
2231 _session->set_session_extents (_session->audible_sample(), loc->end());
2233 XMLNode &after = loc->get_state();
2235 begin_reversible_command (_("Set session start"));
2237 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2239 commit_reversible_command ();
2244 Editor::set_session_end_from_playhead ()
2250 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2251 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2253 XMLNode &before = loc->get_state();
2255 _session->set_session_extents (loc->start(), _session->audible_sample());
2257 XMLNode &after = loc->get_state();
2259 begin_reversible_command (_("Set session start"));
2261 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2263 commit_reversible_command ();
2266 _session->set_end_is_free (false);
2271 Editor::toggle_location_at_playhead_cursor ()
2273 if (!do_remove_location_at_playhead_cursor())
2275 add_location_from_playhead_cursor();
2280 Editor::add_location_from_playhead_cursor ()
2282 add_location_mark (_session->audible_sample());
2286 Editor::do_remove_location_at_playhead_cursor ()
2288 bool removed = false;
2291 XMLNode &before = _session->locations()->get_state();
2293 //find location(s) at this time
2294 Locations::LocationList locs;
2295 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2296 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2297 if ((*i)->is_mark()) {
2298 _session->locations()->remove (*i);
2305 begin_reversible_command (_("remove marker"));
2306 XMLNode &after = _session->locations()->get_state();
2307 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2308 commit_reversible_command ();
2315 Editor::remove_location_at_playhead_cursor ()
2317 do_remove_location_at_playhead_cursor ();
2320 /** Add a range marker around each selected region */
2322 Editor::add_locations_from_region ()
2324 RegionSelection rs = get_regions_from_selection_and_entered ();
2329 bool commit = false;
2331 XMLNode &before = _session->locations()->get_state();
2333 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2335 boost::shared_ptr<Region> region = (*i)->region ();
2337 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2339 _session->locations()->add (location, true);
2344 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2345 XMLNode &after = _session->locations()->get_state();
2346 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2347 commit_reversible_command ();
2351 /** Add a single range marker around all selected regions */
2353 Editor::add_location_from_region ()
2355 RegionSelection rs = get_regions_from_selection_and_entered ();
2361 XMLNode &before = _session->locations()->get_state();
2365 if (rs.size() > 1) {
2366 _session->locations()->next_available_name(markername, "regions");
2368 RegionView* rv = *(rs.begin());
2369 boost::shared_ptr<Region> region = rv->region();
2370 markername = region->name();
2373 if (!choose_new_marker_name(markername)) {
2377 // single range spanning all selected
2378 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2379 _session->locations()->add (location, true);
2381 begin_reversible_command (_("add marker"));
2382 XMLNode &after = _session->locations()->get_state();
2383 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2384 commit_reversible_command ();
2390 Editor::jump_forward_to_mark ()
2396 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2402 _session->request_locate (pos, _session->transport_rolling());
2406 Editor::jump_backward_to_mark ()
2412 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2414 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2415 if (_session->transport_rolling()) {
2416 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2417 samplepos_t prior = _session->locations()->first_mark_before (pos);
2426 _session->request_locate (pos, _session->transport_rolling());
2432 samplepos_t const pos = _session->audible_sample ();
2435 _session->locations()->next_available_name (markername, "mark");
2437 if (!choose_new_marker_name (markername)) {
2441 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2445 Editor::clear_markers ()
2448 begin_reversible_command (_("clear markers"));
2450 XMLNode &before = _session->locations()->get_state();
2451 _session->locations()->clear_markers ();
2452 XMLNode &after = _session->locations()->get_state();
2453 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2455 commit_reversible_command ();
2460 Editor::clear_ranges ()
2463 begin_reversible_command (_("clear ranges"));
2465 XMLNode &before = _session->locations()->get_state();
2467 _session->locations()->clear_ranges ();
2469 XMLNode &after = _session->locations()->get_state();
2470 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2472 commit_reversible_command ();
2477 Editor::clear_locations ()
2479 begin_reversible_command (_("clear locations"));
2481 XMLNode &before = _session->locations()->get_state();
2482 _session->locations()->clear ();
2483 XMLNode &after = _session->locations()->get_state();
2484 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2486 commit_reversible_command ();
2490 Editor::unhide_markers ()
2492 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2493 Location *l = (*i).first;
2494 if (l->is_hidden() && l->is_mark()) {
2495 l->set_hidden(false, this);
2501 Editor::unhide_ranges ()
2503 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2504 Location *l = (*i).first;
2505 if (l->is_hidden() && l->is_range_marker()) {
2506 l->set_hidden(false, this);
2511 /* INSERT/REPLACE */
2514 Editor::insert_region_list_selection (float times)
2516 RouteTimeAxisView *tv = 0;
2517 boost::shared_ptr<Playlist> playlist;
2519 if (clicked_routeview != 0) {
2520 tv = clicked_routeview;
2521 } else if (!selection->tracks.empty()) {
2522 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2525 } else if (entered_track != 0) {
2526 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2533 if ((playlist = tv->playlist()) == 0) {
2537 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2542 begin_reversible_command (_("insert region"));
2543 playlist->clear_changes ();
2544 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2545 if (Config->get_edit_mode() == Ripple)
2546 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2548 _session->add_command(new StatefulDiffCommand (playlist));
2549 commit_reversible_command ();
2552 /* BUILT-IN EFFECTS */
2555 Editor::reverse_selection ()
2560 /* GAIN ENVELOPE EDITING */
2563 Editor::edit_envelope ()
2570 Editor::transition_to_rolling (bool fwd)
2576 if (_session->config.get_external_sync()) {
2577 switch (Config->get_sync_source()) {
2581 /* transport controlled by the master */
2586 if (_session->is_auditioning()) {
2587 _session->cancel_audition ();
2591 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2595 Editor::play_from_start ()
2597 _session->request_locate (_session->current_start_sample(), true);
2601 Editor::play_from_edit_point ()
2603 _session->request_locate (get_preferred_edit_position(), true);
2607 Editor::play_from_edit_point_and_return ()
2609 samplepos_t start_sample;
2610 samplepos_t return_sample;
2612 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2614 if (_session->transport_rolling()) {
2615 _session->request_locate (start_sample, false);
2619 /* don't reset the return sample if its already set */
2621 if ((return_sample = _session->requested_return_sample()) < 0) {
2622 return_sample = _session->audible_sample();
2625 if (start_sample >= 0) {
2626 _session->request_roll_at_and_return (start_sample, return_sample);
2631 Editor::play_selection ()
2633 samplepos_t start, end;
2634 if (!get_selection_extents (start, end))
2637 AudioRange ar (start, end, 0);
2638 list<AudioRange> lar;
2641 _session->request_play_range (&lar, true);
2646 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2648 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2651 location -= _session->preroll_samples (location);
2653 //don't try to locate before the beginning of time
2658 //if follow_playhead is on, keep the playhead on the screen
2659 if (_follow_playhead)
2660 if (location < _leftmost_sample)
2661 location = _leftmost_sample;
2663 _session->request_locate (location);
2667 Editor::play_with_preroll ()
2669 samplepos_t start, end;
2670 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2671 const samplepos_t preroll = _session->preroll_samples (start);
2673 samplepos_t ret = start;
2675 if (start > preroll) {
2676 start = start - preroll;
2679 end = end + preroll; //"post-roll"
2681 AudioRange ar (start, end, 0);
2682 list<AudioRange> lar;
2685 _session->request_play_range (&lar, true);
2686 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2688 samplepos_t ph = playhead_cursor->current_sample ();
2689 const samplepos_t preroll = _session->preroll_samples (ph);
2692 start = ph - preroll;
2696 _session->request_locate (start, true);
2697 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2702 Editor::rec_with_preroll ()
2704 samplepos_t ph = playhead_cursor->current_sample ();
2705 samplepos_t preroll = _session->preroll_samples (ph);
2706 _session->request_preroll_record_trim (ph, preroll);
2710 Editor::rec_with_count_in ()
2712 _session->request_count_in_record ();
2716 Editor::play_location (Location& location)
2718 if (location.start() <= location.end()) {
2722 _session->request_bounded_roll (location.start(), location.end());
2726 Editor::loop_location (Location& location)
2728 if (location.start() <= location.end()) {
2734 if ((tll = transport_loop_location()) != 0) {
2735 tll->set (location.start(), location.end());
2737 // enable looping, reposition and start rolling
2738 _session->request_locate (tll->start(), true);
2739 _session->request_play_loop (true);
2744 Editor::do_layer_operation (LayerOperation op)
2746 if (selection->regions.empty ()) {
2750 bool const multiple = selection->regions.size() > 1;
2754 begin_reversible_command (_("raise regions"));
2756 begin_reversible_command (_("raise region"));
2762 begin_reversible_command (_("raise regions to top"));
2764 begin_reversible_command (_("raise region to top"));
2770 begin_reversible_command (_("lower regions"));
2772 begin_reversible_command (_("lower region"));
2778 begin_reversible_command (_("lower regions to bottom"));
2780 begin_reversible_command (_("lower region"));
2785 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2786 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2787 (*i)->clear_owned_changes ();
2790 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2791 boost::shared_ptr<Region> r = (*i)->region ();
2803 r->lower_to_bottom ();
2807 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2808 vector<Command*> cmds;
2810 _session->add_commands (cmds);
2813 commit_reversible_command ();
2817 Editor::raise_region ()
2819 do_layer_operation (Raise);
2823 Editor::raise_region_to_top ()
2825 do_layer_operation (RaiseToTop);
2829 Editor::lower_region ()
2831 do_layer_operation (Lower);
2835 Editor::lower_region_to_bottom ()
2837 do_layer_operation (LowerToBottom);
2840 /** Show the region editor for the selected regions */
2842 Editor::show_region_properties ()
2844 selection->foreach_regionview (&RegionView::show_region_editor);
2847 /** Show the midi list editor for the selected MIDI regions */
2849 Editor::show_midi_list_editor ()
2851 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2855 Editor::rename_region ()
2857 RegionSelection rs = get_regions_from_selection_and_entered ();
2863 ArdourDialog d (_("Rename Region"), true, false);
2865 Label label (_("New name:"));
2868 hbox.set_spacing (6);
2869 hbox.pack_start (label, false, false);
2870 hbox.pack_start (entry, true, true);
2872 d.get_vbox()->set_border_width (12);
2873 d.get_vbox()->pack_start (hbox, false, false);
2875 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2876 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2878 d.set_size_request (300, -1);
2880 entry.set_text (rs.front()->region()->name());
2881 entry.select_region (0, -1);
2883 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2889 int const ret = d.run();
2893 if (ret != RESPONSE_OK) {
2897 std::string str = entry.get_text();
2898 strip_whitespace_edges (str);
2900 rs.front()->region()->set_name (str);
2901 _regions->redisplay ();
2905 /** Start an audition of the first selected region */
2907 Editor::play_edit_range ()
2909 samplepos_t start, end;
2911 if (get_edit_op_range (start, end)) {
2912 _session->request_bounded_roll (start, end);
2917 Editor::play_selected_region ()
2919 samplepos_t start = max_samplepos;
2920 samplepos_t end = 0;
2922 RegionSelection rs = get_regions_from_selection_and_entered ();
2928 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2929 if ((*i)->region()->position() < start) {
2930 start = (*i)->region()->position();
2932 if ((*i)->region()->last_sample() + 1 > end) {
2933 end = (*i)->region()->last_sample() + 1;
2937 _session->request_bounded_roll (start, end);
2941 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2943 _session->audition_region (region);
2947 Editor::region_from_selection ()
2949 if (clicked_axisview == 0) {
2953 if (selection->time.empty()) {
2957 samplepos_t start = selection->time[clicked_selection].start;
2958 samplepos_t end = selection->time[clicked_selection].end;
2960 TrackViewList tracks = get_tracks_for_range_action ();
2962 samplepos_t selection_cnt = end - start + 1;
2964 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2965 boost::shared_ptr<Region> current;
2966 boost::shared_ptr<Playlist> pl;
2967 samplepos_t internal_start;
2970 if ((pl = (*i)->playlist()) == 0) {
2974 if ((current = pl->top_region_at (start)) == 0) {
2978 internal_start = start - current->position();
2979 RegionFactory::region_name (new_name, current->name(), true);
2983 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2984 plist.add (ARDOUR::Properties::length, selection_cnt);
2985 plist.add (ARDOUR::Properties::name, new_name);
2986 plist.add (ARDOUR::Properties::layer, 0);
2988 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2993 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2995 if (selection->time.empty() || selection->tracks.empty()) {
2999 samplepos_t start, end;
3000 if (clicked_selection) {
3001 start = selection->time[clicked_selection].start;
3002 end = selection->time[clicked_selection].end;
3004 start = selection->time.start();
3005 end = selection->time.end_sample();
3008 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3009 sort_track_selection (ts);
3011 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3012 boost::shared_ptr<Region> current;
3013 boost::shared_ptr<Playlist> playlist;
3014 samplepos_t internal_start;
3017 if ((playlist = (*i)->playlist()) == 0) {
3021 if ((current = playlist->top_region_at(start)) == 0) {
3025 internal_start = start - current->position();
3026 RegionFactory::region_name (new_name, current->name(), true);
3030 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3031 plist.add (ARDOUR::Properties::length, end - start + 1);
3032 plist.add (ARDOUR::Properties::name, new_name);
3034 new_regions.push_back (RegionFactory::create (current, plist));
3039 Editor::split_multichannel_region ()
3041 RegionSelection rs = get_regions_from_selection_and_entered ();
3047 vector< boost::shared_ptr<Region> > v;
3049 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3050 (*x)->region()->separate_by_channel (v);
3055 Editor::new_region_from_selection ()
3057 region_from_selection ();
3058 cancel_selection ();
3062 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3064 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3065 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3066 case Evoral::OverlapNone:
3074 * - selected tracks, or if there are none...
3075 * - tracks containing selected regions, or if there are none...
3080 Editor::get_tracks_for_range_action () const
3084 if (selection->tracks.empty()) {
3086 /* use tracks with selected regions */
3088 RegionSelection rs = selection->regions;
3090 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3091 TimeAxisView* tv = &(*i)->get_time_axis_view();
3093 if (!t.contains (tv)) {
3099 /* no regions and no tracks: use all tracks */
3105 t = selection->tracks;
3108 return t.filter_to_unique_playlists();
3112 Editor::separate_regions_between (const TimeSelection& ts)
3114 bool in_command = false;
3115 boost::shared_ptr<Playlist> playlist;
3116 RegionSelection new_selection;
3118 TrackViewList tmptracks = get_tracks_for_range_action ();
3119 sort_track_selection (tmptracks);
3121 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3123 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3129 if (!rtv->is_track()) {
3133 /* no edits to destructive tracks */
3135 if (rtv->track()->destructive()) {
3139 if ((playlist = rtv->playlist()) != 0) {
3141 playlist->clear_changes ();
3143 /* XXX need to consider musical time selections here at some point */
3145 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3147 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3148 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3150 latest_regionviews.clear ();
3152 playlist->partition ((*t).start, (*t).end, false);
3156 if (!latest_regionviews.empty()) {
3158 rtv->view()->foreach_regionview (sigc::bind (
3159 sigc::ptr_fun (add_if_covered),
3160 &(*t), &new_selection));
3163 begin_reversible_command (_("separate"));
3167 /* pick up changes to existing regions */
3169 vector<Command*> cmds;
3170 playlist->rdiff (cmds);
3171 _session->add_commands (cmds);
3173 /* pick up changes to the playlist itself (adds/removes)
3176 _session->add_command(new StatefulDiffCommand (playlist));
3183 // selection->set (new_selection);
3185 commit_reversible_command ();
3189 struct PlaylistState {
3190 boost::shared_ptr<Playlist> playlist;
3194 /** Take tracks from get_tracks_for_range_action and cut any regions
3195 * on those tracks so that the tracks are empty over the time
3199 Editor::separate_region_from_selection ()
3201 /* preferentially use *all* ranges in the time selection if we're in range mode
3202 to allow discontiguous operation, since get_edit_op_range() currently
3203 returns a single range.
3206 if (!selection->time.empty()) {
3208 separate_regions_between (selection->time);
3215 if (get_edit_op_range (start, end)) {
3217 AudioRange ar (start, end, 1);
3221 separate_regions_between (ts);
3227 Editor::separate_region_from_punch ()
3229 Location* loc = _session->locations()->auto_punch_location();
3231 separate_regions_using_location (*loc);
3236 Editor::separate_region_from_loop ()
3238 Location* loc = _session->locations()->auto_loop_location();
3240 separate_regions_using_location (*loc);
3245 Editor::separate_regions_using_location (Location& loc)
3247 if (loc.is_mark()) {
3251 AudioRange ar (loc.start(), loc.end(), 1);
3256 separate_regions_between (ts);
3259 /** Separate regions under the selected region */
3261 Editor::separate_under_selected_regions ()
3263 vector<PlaylistState> playlists;
3267 rs = get_regions_from_selection_and_entered();
3269 if (!_session || rs.empty()) {
3273 begin_reversible_command (_("separate region under"));
3275 list<boost::shared_ptr<Region> > regions_to_remove;
3277 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3278 // we can't just remove the region(s) in this loop because
3279 // this removes them from the RegionSelection, and they thus
3280 // disappear from underneath the iterator, and the ++i above
3281 // SEGVs in a puzzling fashion.
3283 // so, first iterate over the regions to be removed from rs and
3284 // add them to the regions_to_remove list, and then
3285 // iterate over the list to actually remove them.
3287 regions_to_remove.push_back ((*i)->region());
3290 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3292 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3295 // is this check necessary?
3299 vector<PlaylistState>::iterator i;
3301 //only take state if this is a new playlist.
3302 for (i = playlists.begin(); i != playlists.end(); ++i) {
3303 if ((*i).playlist == playlist) {
3308 if (i == playlists.end()) {
3310 PlaylistState before;
3311 before.playlist = playlist;
3312 before.before = &playlist->get_state();
3313 playlist->clear_changes ();
3314 playlist->freeze ();
3315 playlists.push_back(before);
3318 //Partition on the region bounds
3319 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3321 //Re-add region that was just removed due to the partition operation
3322 playlist->add_region ((*rl), (*rl)->first_sample());
3325 vector<PlaylistState>::iterator pl;
3327 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3328 (*pl).playlist->thaw ();
3329 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3332 commit_reversible_command ();
3336 Editor::crop_region_to_selection ()
3338 if (!selection->time.empty()) {
3340 begin_reversible_command (_("Crop Regions to Time Selection"));
3341 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3342 crop_region_to ((*i).start, (*i).end);
3344 commit_reversible_command();
3350 if (get_edit_op_range (start, end)) {
3351 begin_reversible_command (_("Crop Regions to Edit Range"));
3353 crop_region_to (start, end);
3355 commit_reversible_command();
3362 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3364 vector<boost::shared_ptr<Playlist> > playlists;
3365 boost::shared_ptr<Playlist> playlist;
3368 if (selection->tracks.empty()) {
3369 ts = track_views.filter_to_unique_playlists();
3371 ts = selection->tracks.filter_to_unique_playlists ();
3374 sort_track_selection (ts);
3376 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3378 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3384 boost::shared_ptr<Track> t = rtv->track();
3386 if (t != 0 && ! t->destructive()) {
3388 if ((playlist = rtv->playlist()) != 0) {
3389 playlists.push_back (playlist);
3394 if (playlists.empty()) {
3399 samplepos_t new_start;
3400 samplepos_t new_end;
3401 samplecnt_t new_length;
3403 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3405 /* Only the top regions at start and end have to be cropped */
3406 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3407 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3409 vector<boost::shared_ptr<Region> > regions;
3411 if (region_at_start != 0) {
3412 regions.push_back (region_at_start);
3414 if (region_at_end != 0) {
3415 regions.push_back (region_at_end);
3418 /* now adjust lengths */
3419 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3421 pos = (*i)->position();
3422 new_start = max (start, pos);
3423 if (max_samplepos - pos > (*i)->length()) {
3424 new_end = pos + (*i)->length() - 1;
3426 new_end = max_samplepos;
3428 new_end = min (end, new_end);
3429 new_length = new_end - new_start + 1;
3431 (*i)->clear_changes ();
3432 (*i)->trim_to (new_start, new_length);
3433 _session->add_command (new StatefulDiffCommand (*i));
3439 Editor::region_fill_track ()
3441 boost::shared_ptr<Playlist> playlist;
3442 RegionSelection regions = get_regions_from_selection_and_entered ();
3443 RegionSelection foo;
3445 samplepos_t const end = _session->current_end_sample ();
3447 if (regions.empty () || regions.end_sample () + 1 >= end) {
3451 samplepos_t const start_sample = regions.start ();
3452 samplepos_t const end_sample = regions.end_sample ();
3453 samplecnt_t const gap = end_sample - start_sample + 1;
3455 begin_reversible_command (Operations::region_fill);
3457 selection->clear_regions ();
3459 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3461 boost::shared_ptr<Region> r ((*i)->region());
3463 TimeAxisView& tv = (*i)->get_time_axis_view();
3464 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3465 latest_regionviews.clear ();
3466 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3468 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3469 playlist = (*i)->region()->playlist();
3470 playlist->clear_changes ();
3471 playlist->duplicate_until (r, position, gap, end);
3472 _session->add_command(new StatefulDiffCommand (playlist));
3476 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3480 selection->set (foo);
3483 commit_reversible_command ();
3487 Editor::set_region_sync_position ()
3489 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3493 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3495 bool in_command = false;
3497 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3499 if (!(*r)->region()->covers (where)) {
3503 boost::shared_ptr<Region> region ((*r)->region());
3506 begin_reversible_command (_("set sync point"));
3510 region->clear_changes ();
3511 region->set_sync_position (where);
3512 _session->add_command(new StatefulDiffCommand (region));
3516 commit_reversible_command ();
3520 /** Remove the sync positions of the selection */
3522 Editor::remove_region_sync ()
3524 RegionSelection rs = get_regions_from_selection_and_entered ();
3530 begin_reversible_command (_("remove region sync"));
3532 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3534 (*i)->region()->clear_changes ();
3535 (*i)->region()->clear_sync_position ();
3536 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3539 commit_reversible_command ();
3543 Editor::naturalize_region ()
3545 RegionSelection rs = get_regions_from_selection_and_entered ();
3551 if (rs.size() > 1) {
3552 begin_reversible_command (_("move regions to original position"));
3554 begin_reversible_command (_("move region to original position"));
3557 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3558 (*i)->region()->clear_changes ();
3559 (*i)->region()->move_to_natural_position ();
3560 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3563 commit_reversible_command ();
3567 Editor::align_regions (RegionPoint what)
3569 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3575 begin_reversible_command (_("align selection"));
3577 samplepos_t const position = get_preferred_edit_position ();
3579 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3580 align_region_internal ((*i)->region(), what, position);
3583 commit_reversible_command ();
3586 struct RegionSortByTime {
3587 bool operator() (const RegionView* a, const RegionView* b) {
3588 return a->region()->position() < b->region()->position();
3593 Editor::align_regions_relative (RegionPoint point)
3595 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3601 samplepos_t const position = get_preferred_edit_position ();
3603 samplepos_t distance = 0;
3604 samplepos_t pos = 0;
3607 list<RegionView*> sorted;
3608 rs.by_position (sorted);
3610 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3615 if (position > r->position()) {
3616 distance = position - r->position();
3618 distance = r->position() - position;
3624 if (position > r->last_sample()) {
3625 distance = position - r->last_sample();
3626 pos = r->position() + distance;
3628 distance = r->last_sample() - position;
3629 pos = r->position() - distance;
3635 pos = r->adjust_to_sync (position);
3636 if (pos > r->position()) {
3637 distance = pos - r->position();
3639 distance = r->position() - pos;
3645 if (pos == r->position()) {
3649 begin_reversible_command (_("align selection (relative)"));
3651 /* move first one specially */
3653 r->clear_changes ();
3654 r->set_position (pos);
3655 _session->add_command(new StatefulDiffCommand (r));
3657 /* move rest by the same amount */
3661 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3663 boost::shared_ptr<Region> region ((*i)->region());
3665 region->clear_changes ();
3668 region->set_position (region->position() + distance);
3670 region->set_position (region->position() - distance);
3673 _session->add_command(new StatefulDiffCommand (region));
3677 commit_reversible_command ();
3681 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3683 begin_reversible_command (_("align region"));
3684 align_region_internal (region, point, position);
3685 commit_reversible_command ();
3689 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3691 region->clear_changes ();
3695 region->set_position (region->adjust_to_sync (position));
3699 if (position > region->length()) {
3700 region->set_position (position - region->length());
3705 region->set_position (position);
3709 _session->add_command(new StatefulDiffCommand (region));
3713 Editor::trim_region_front ()
3719 Editor::trim_region_back ()
3721 trim_region (false);
3725 Editor::trim_region (bool front)
3727 samplepos_t where = get_preferred_edit_position();
3728 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3734 begin_reversible_command (front ? _("trim front") : _("trim back"));
3736 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3737 if (!(*i)->region()->locked()) {
3739 (*i)->region()->clear_changes ();
3742 (*i)->region()->trim_front (where);
3744 (*i)->region()->trim_end (where);
3747 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3751 commit_reversible_command ();
3754 /** Trim the end of the selected regions to the position of the edit cursor */
3756 Editor::trim_region_to_loop ()
3758 Location* loc = _session->locations()->auto_loop_location();
3762 trim_region_to_location (*loc, _("trim to loop"));
3766 Editor::trim_region_to_punch ()
3768 Location* loc = _session->locations()->auto_punch_location();
3772 trim_region_to_location (*loc, _("trim to punch"));
3776 Editor::trim_region_to_location (const Location& loc, const char* str)
3778 RegionSelection rs = get_regions_from_selection_and_entered ();
3779 bool in_command = false;
3781 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3782 RegionView* rv = (*x);
3784 /* require region to span proposed trim */
3785 switch (rv->region()->coverage (loc.start(), loc.end())) {
3786 case Evoral::OverlapInternal:
3792 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3800 start = loc.start();
3803 rv->region()->clear_changes ();
3804 rv->region()->trim_to (start, (end - start));
3807 begin_reversible_command (str);
3810 _session->add_command(new StatefulDiffCommand (rv->region()));
3814 commit_reversible_command ();
3819 Editor::trim_region_to_previous_region_end ()
3821 return trim_to_region(false);
3825 Editor::trim_region_to_next_region_start ()
3827 return trim_to_region(true);
3831 Editor::trim_to_region(bool forward)
3833 RegionSelection rs = get_regions_from_selection_and_entered ();
3834 bool in_command = false;
3836 boost::shared_ptr<Region> next_region;
3838 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3840 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3846 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3852 boost::shared_ptr<Region> region = arv->region();
3853 boost::shared_ptr<Playlist> playlist (region->playlist());
3855 region->clear_changes ();
3859 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3865 region->trim_end (next_region->first_sample() - 1);
3866 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3870 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3876 region->trim_front (next_region->last_sample() + 1);
3877 arv->region_changed (ARDOUR::bounds_change);
3881 begin_reversible_command (_("trim to region"));
3884 _session->add_command(new StatefulDiffCommand (region));
3888 commit_reversible_command ();
3893 Editor::unfreeze_route ()
3895 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3899 clicked_routeview->track()->unfreeze ();
3903 Editor::_freeze_thread (void* arg)
3905 return static_cast<Editor*>(arg)->freeze_thread ();
3909 Editor::freeze_thread ()
3911 /* create event pool because we may need to talk to the session */
3912 SessionEvent::create_per_thread_pool ("freeze events", 64);
3913 /* create per-thread buffers for process() tree to use */
3914 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3915 current_interthread_info->done = true;
3920 Editor::freeze_route ()
3926 /* stop transport before we start. this is important */
3928 _session->request_transport_speed (0.0);
3930 /* wait for just a little while, because the above call is asynchronous */
3932 Glib::usleep (250000);
3934 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3938 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3940 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3941 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3943 d.set_title (_("Cannot freeze"));
3948 if (clicked_routeview->track()->has_external_redirects()) {
3949 MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
3950 "Freezing will only process the signal as far as the first send/insert/return."),
3951 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3953 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3954 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3955 d.set_title (_("Freeze Limits"));
3957 int response = d.run ();
3960 case Gtk::RESPONSE_CANCEL:
3967 InterThreadInfo itt;
3968 current_interthread_info = &itt;
3970 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3972 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3974 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3976 while (!itt.done && !itt.cancel) {
3977 gtk_main_iteration ();
3980 pthread_join (itt.thread, 0);
3981 current_interthread_info = 0;
3985 Editor::bounce_range_selection (bool replace, bool enable_processing)
3987 if (selection->time.empty()) {
3991 TrackSelection views = selection->tracks;
3993 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3995 if (enable_processing) {
3997 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3999 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4001 _("You can't perform this operation because the processing of the signal "
4002 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4003 "You can do this without processing, which is a different operation.")
4005 d.set_title (_("Cannot bounce"));
4012 samplepos_t start = selection->time[clicked_selection].start;
4013 samplepos_t end = selection->time[clicked_selection].end;
4014 samplepos_t cnt = end - start + 1;
4015 bool in_command = false;
4017 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4019 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4025 boost::shared_ptr<Playlist> playlist;
4027 if ((playlist = rtv->playlist()) == 0) {
4031 InterThreadInfo itt;
4033 playlist->clear_changes ();
4034 playlist->clear_owned_changes ();
4036 boost::shared_ptr<Region> r;
4038 if (enable_processing) {
4039 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4041 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4049 list<AudioRange> ranges;
4050 ranges.push_back (AudioRange (start, start+cnt, 0));
4051 playlist->cut (ranges); // discard result
4052 playlist->add_region (r, start);
4056 begin_reversible_command (_("bounce range"));
4059 vector<Command*> cmds;
4060 playlist->rdiff (cmds);
4061 _session->add_commands (cmds);
4063 _session->add_command (new StatefulDiffCommand (playlist));
4067 commit_reversible_command ();
4071 /** Delete selected regions, automation points or a time range */
4075 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4076 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4077 bool deleted = false;
4078 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4079 deleted = current_mixer_strip->delete_processors ();
4085 /** Cut selected regions, automation points or a time range */
4092 /** Copy selected regions, automation points or a time range */
4100 /** @return true if a Cut, Copy or Clear is possible */
4102 Editor::can_cut_copy () const
4104 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4111 /** Cut, copy or clear selected regions, automation points or a time range.
4112 * @param op Operation (Delete, Cut, Copy or Clear)
4115 Editor::cut_copy (CutCopyOp op)
4117 /* only cancel selection if cut/copy is successful.*/
4123 opname = _("delete");
4132 opname = _("clear");
4136 /* if we're deleting something, and the mouse is still pressed,
4137 the thing we started a drag for will be gone when we release
4138 the mouse button(s). avoid this. see part 2 at the end of
4142 if (op == Delete || op == Cut || op == Clear) {
4143 if (_drags->active ()) {
4148 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4149 cut_buffer->clear ();
4152 if (entered_marker) {
4154 /* cut/delete op while pointing at a marker */
4157 Location* loc = find_location_from_marker (entered_marker, ignored);
4159 if (_session && loc) {
4160 entered_marker = NULL;
4161 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4168 switch (mouse_mode) {
4171 begin_reversible_command (opname + ' ' + X_("MIDI"));
4173 commit_reversible_command ();
4179 bool did_edit = false;
4181 if (!selection->regions.empty() || !selection->points.empty()) {
4182 begin_reversible_command (opname + ' ' + _("objects"));
4185 if (!selection->regions.empty()) {
4186 cut_copy_regions (op, selection->regions);
4188 if (op == Cut || op == Delete) {
4189 selection->clear_regions ();
4193 if (!selection->points.empty()) {
4194 cut_copy_points (op);
4196 if (op == Cut || op == Delete) {
4197 selection->clear_points ();
4200 } else if (selection->time.empty()) {
4201 samplepos_t start, end;
4202 /* no time selection, see if we can get an edit range
4205 if (get_edit_op_range (start, end)) {
4206 selection->set (start, end);
4208 } else if (!selection->time.empty()) {
4209 begin_reversible_command (opname + ' ' + _("range"));
4212 cut_copy_ranges (op);
4214 if (op == Cut || op == Delete) {
4215 selection->clear_time ();
4220 /* reset repeated paste state */
4222 last_paste_pos = -1;
4223 commit_reversible_command ();
4226 if (op == Delete || op == Cut || op == Clear) {
4232 struct AutomationRecord {
4233 AutomationRecord () : state (0) , line(NULL) {}
4234 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4236 XMLNode* state; ///< state before any operation
4237 const AutomationLine* line; ///< line this came from
4238 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4241 struct PointsSelectionPositionSorter {
4242 bool operator() (ControlPoint* a, ControlPoint* b) {
4243 return (*(a->model()))->when < (*(b->model()))->when;
4247 /** Cut, copy or clear selected automation points.
4248 * @param op Operation (Cut, Copy or Clear)
4251 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4253 if (selection->points.empty ()) {
4257 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4258 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4260 /* Keep a record of the AutomationLists that we end up using in this operation */
4261 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4264 /* user could select points in any order */
4265 selection->points.sort(PointsSelectionPositionSorter ());
4267 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4268 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4269 const AutomationLine& line = (*sel_point)->line();
4270 const boost::shared_ptr<AutomationList> al = line.the_list();
4271 if (lists.find (al) == lists.end ()) {
4272 /* We haven't seen this list yet, so make a record for it. This includes
4273 taking a copy of its current state, in case this is needed for undo later.
4275 lists[al] = AutomationRecord (&al->get_state (), &line);
4279 if (op == Cut || op == Copy) {
4280 /* This operation will involve putting things in the cut buffer, so create an empty
4281 ControlList for each of our source lists to put the cut buffer data in.
4283 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4284 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4287 /* Add all selected points to the relevant copy ControlLists */
4288 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4289 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4290 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4291 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4293 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4295 /* Update earliest MIDI start time in beats */
4296 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4298 /* Update earliest session start time in samples */
4299 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4303 /* Snap start time backwards, so copy/paste is snap aligned. */
4305 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4306 earliest = Temporal::Beats(); // Weird... don't offset
4308 earliest.round_down_to_beat();
4310 if (start.sample == std::numeric_limits<double>::max()) {
4311 start.sample = 0; // Weird... don't offset
4313 snap_to(start, RoundDownMaybe);
4316 const double line_offset = midi ? earliest.to_double() : start.sample;
4317 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4318 /* Correct this copy list so that it is relative to the earliest
4319 start time, so relative ordering between points is preserved
4320 when copying from several lists and the paste starts at the
4321 earliest copied piece of data. */
4322 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4323 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4324 (*ctrl_evt)->when -= line_offset;
4327 /* And add it to the cut buffer */
4328 cut_buffer->add (al_cpy);
4332 if (op == Delete || op == Cut) {
4333 /* This operation needs to remove things from the main AutomationList, so do that now */
4335 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4336 i->first->freeze ();
4339 /* Remove each selected point from its AutomationList */
4340 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4341 AutomationLine& line = (*sel_point)->line ();
4342 boost::shared_ptr<AutomationList> al = line.the_list();
4346 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4347 /* removing of first and last gain point in region gain lines is prohibited*/
4348 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4354 al->erase ((*sel_point)->model ());
4358 /* Thaw the lists and add undo records for them */
4359 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4360 boost::shared_ptr<AutomationList> al = i->first;
4362 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4367 /** Cut, copy or clear selected automation points.
4368 * @param op Operation (Cut, Copy or Clear)
4371 Editor::cut_copy_midi (CutCopyOp op)
4373 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4374 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4375 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4377 if (!mrv->selection().empty()) {
4378 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4380 mrv->cut_copy_clear (op);
4382 /* XXX: not ideal, as there may be more than one track involved in the selection */
4383 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4387 if (!selection->points.empty()) {
4388 cut_copy_points (op, earliest, true);
4389 if (op == Cut || op == Delete) {
4390 selection->clear_points ();
4395 struct lt_playlist {
4396 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4397 return a.playlist < b.playlist;
4401 struct PlaylistMapping {
4403 boost::shared_ptr<Playlist> pl;
4405 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4408 /** Remove `clicked_regionview' */
4410 Editor::remove_clicked_region ()
4412 if (clicked_routeview == 0 || clicked_regionview == 0) {
4416 begin_reversible_command (_("remove region"));
4418 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4420 playlist->clear_changes ();
4421 playlist->clear_owned_changes ();
4422 playlist->remove_region (clicked_regionview->region());
4423 if (Config->get_edit_mode() == Ripple)
4424 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4426 /* We might have removed regions, which alters other regions' layering_index,
4427 so we need to do a recursive diff here.
4429 vector<Command*> cmds;
4430 playlist->rdiff (cmds);
4431 _session->add_commands (cmds);
4433 _session->add_command(new StatefulDiffCommand (playlist));
4434 commit_reversible_command ();
4438 /** Remove the selected regions */
4440 Editor::remove_selected_regions ()
4442 RegionSelection rs = get_regions_from_selection_and_entered ();
4444 if (!_session || rs.empty()) {
4448 list<boost::shared_ptr<Region> > regions_to_remove;
4450 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4451 // we can't just remove the region(s) in this loop because
4452 // this removes them from the RegionSelection, and they thus
4453 // disappear from underneath the iterator, and the ++i above
4454 // SEGVs in a puzzling fashion.
4456 // so, first iterate over the regions to be removed from rs and
4457 // add them to the regions_to_remove list, and then
4458 // iterate over the list to actually remove them.
4460 regions_to_remove.push_back ((*i)->region());
4463 vector<boost::shared_ptr<Playlist> > playlists;
4465 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4467 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4470 // is this check necessary?
4474 /* get_regions_from_selection_and_entered() guarantees that
4475 the playlists involved are unique, so there is no need
4479 playlists.push_back (playlist);
4481 playlist->clear_changes ();
4482 playlist->clear_owned_changes ();
4483 playlist->freeze ();
4484 playlist->remove_region (*rl);
4485 if (Config->get_edit_mode() == Ripple)
4486 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4490 vector<boost::shared_ptr<Playlist> >::iterator pl;
4491 bool in_command = false;
4493 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4496 /* We might have removed regions, which alters other regions' layering_index,
4497 so we need to do a recursive diff here.
4501 begin_reversible_command (_("remove region"));
4504 vector<Command*> cmds;
4505 (*pl)->rdiff (cmds);
4506 _session->add_commands (cmds);
4508 _session->add_command(new StatefulDiffCommand (*pl));
4512 commit_reversible_command ();
4516 /** Cut, copy or clear selected regions.
4517 * @param op Operation (Cut, Copy or Clear)
4520 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4522 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4523 a map when we want ordered access to both elements. i think.
4526 vector<PlaylistMapping> pmap;
4528 samplepos_t first_position = max_samplepos;
4530 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4531 FreezeList freezelist;
4533 /* get ordering correct before we cut/copy */
4535 rs.sort_by_position_and_track ();
4537 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4539 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4541 if (op == Cut || op == Clear || op == Delete) {
4542 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4545 FreezeList::iterator fl;
4547 // only take state if this is a new playlist.
4548 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4554 if (fl == freezelist.end()) {
4555 pl->clear_changes();
4556 pl->clear_owned_changes ();
4558 freezelist.insert (pl);
4563 TimeAxisView* tv = &(*x)->get_time_axis_view();
4564 vector<PlaylistMapping>::iterator z;
4566 for (z = pmap.begin(); z != pmap.end(); ++z) {
4567 if ((*z).tv == tv) {
4572 if (z == pmap.end()) {
4573 pmap.push_back (PlaylistMapping (tv));
4577 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4579 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4582 /* region not yet associated with a playlist (e.g. unfinished
4589 TimeAxisView& tv = (*x)->get_time_axis_view();
4590 boost::shared_ptr<Playlist> npl;
4591 RegionSelection::iterator tmp;
4598 vector<PlaylistMapping>::iterator z;
4600 for (z = pmap.begin(); z != pmap.end(); ++z) {
4601 if ((*z).tv == &tv) {
4606 assert (z != pmap.end());
4609 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4617 boost::shared_ptr<Region> r = (*x)->region();
4618 boost::shared_ptr<Region> _xx;
4624 pl->remove_region (r);
4625 if (Config->get_edit_mode() == Ripple)
4626 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4630 _xx = RegionFactory::create (r);
4631 npl->add_region (_xx, r->position() - first_position);
4632 pl->remove_region (r);
4633 if (Config->get_edit_mode() == Ripple)
4634 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4638 /* copy region before adding, so we're not putting same object into two different playlists */
4639 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4643 pl->remove_region (r);
4644 if (Config->get_edit_mode() == Ripple)
4645 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4654 list<boost::shared_ptr<Playlist> > foo;
4656 /* the pmap is in the same order as the tracks in which selected regions occurred */
4658 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4661 foo.push_back ((*i).pl);
4666 cut_buffer->set (foo);
4670 _last_cut_copy_source_track = 0;
4672 _last_cut_copy_source_track = pmap.front().tv;
4676 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4679 /* We might have removed regions, which alters other regions' layering_index,
4680 so we need to do a recursive diff here.
4682 vector<Command*> cmds;
4683 (*pl)->rdiff (cmds);
4684 _session->add_commands (cmds);
4686 _session->add_command (new StatefulDiffCommand (*pl));
4691 Editor::cut_copy_ranges (CutCopyOp op)
4693 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4695 /* Sort the track selection now, so that it if is used, the playlists
4696 selected by the calls below to cut_copy_clear are in the order that
4697 their tracks appear in the editor. This makes things like paste
4698 of ranges work properly.
4701 sort_track_selection (ts);
4704 if (!entered_track) {
4707 ts.push_back (entered_track);
4710 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4711 (*i)->cut_copy_clear (*selection, op);
4716 Editor::paste (float times, bool from_context)
4718 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4719 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4720 paste_internal (where.sample, times, 0);
4724 Editor::mouse_paste ()
4726 MusicSample where (0, 0);
4728 if (!mouse_sample (where.sample, ignored)) {
4733 paste_internal (where.sample, 1, where.division);
4737 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4739 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4741 if (cut_buffer->empty(internal_editing())) {
4745 if (position == max_samplepos) {
4746 position = get_preferred_edit_position();
4747 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4750 if (position == last_paste_pos) {
4751 /* repeated paste in the same position */
4754 /* paste in new location, reset repeated paste state */
4756 last_paste_pos = position;
4759 /* get everything in the correct order */
4762 if (!selection->tracks.empty()) {
4763 /* If there is a track selection, paste into exactly those tracks and
4764 * only those tracks. This allows the user to be explicit and override
4765 * the below "do the reasonable thing" logic. */
4766 ts = selection->tracks.filter_to_unique_playlists ();
4767 sort_track_selection (ts);
4769 /* Figure out which track to base the paste at. */
4770 TimeAxisView* base_track = NULL;
4771 if (_edit_point == Editing::EditAtMouse && entered_track) {
4772 /* With the mouse edit point, paste onto the track under the mouse. */
4773 base_track = entered_track;
4774 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4775 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4776 base_track = &entered_regionview->get_time_axis_view();
4777 } else if (_last_cut_copy_source_track) {
4778 /* Paste to the track that the cut/copy came from (see mantis #333). */
4779 base_track = _last_cut_copy_source_track;
4781 /* This is "impossible" since we've copied... well, do nothing. */
4785 /* Walk up to parent if necessary, so base track is a route. */
4786 while (base_track->get_parent()) {
4787 base_track = base_track->get_parent();
4790 /* Add base track and all tracks below it. The paste logic will select
4791 the appropriate object types from the cut buffer in relative order. */
4792 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4793 if ((*i)->order() >= base_track->order()) {
4798 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4799 sort_track_selection (ts);
4801 /* Add automation children of each track in order, for pasting several lines. */
4802 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4803 /* Add any automation children for pasting several lines */
4804 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4809 typedef RouteTimeAxisView::AutomationTracks ATracks;
4810 const ATracks& atracks = rtv->automation_tracks();
4811 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4812 i = ts.insert(i, a->second.get());
4817 /* We now have a list of trackviews starting at base_track, including
4818 automation children, in the order shown in the editor, e.g. R1,
4819 R1.A1, R1.A2, R2, R2.A1, ... */
4822 begin_reversible_command (Operations::paste);
4824 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4825 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4826 /* Only one line copied, and one automation track selected. Do a
4827 "greedy" paste from one automation type to another. */
4829 PasteContext ctx(paste_count, times, ItemCounts(), true);
4830 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4834 /* Paste into tracks */
4836 PasteContext ctx(paste_count, times, ItemCounts(), false);
4837 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4838 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4842 commit_reversible_command ();
4846 Editor::duplicate_regions (float times)
4848 RegionSelection rs (get_regions_from_selection_and_entered());
4849 duplicate_some_regions (rs, times);
4853 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4855 if (regions.empty ()) {
4859 boost::shared_ptr<Playlist> playlist;
4860 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4861 RegionSelection foo;
4863 samplepos_t const start_sample = regions.start ();
4864 samplepos_t const end_sample = regions.end_sample ();
4865 samplecnt_t const gap = end_sample - start_sample + 1;
4867 begin_reversible_command (Operations::duplicate_region);
4869 selection->clear_regions ();
4871 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4873 boost::shared_ptr<Region> r ((*i)->region());
4875 TimeAxisView& tv = (*i)->get_time_axis_view();
4876 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4877 latest_regionviews.clear ();
4878 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4880 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4881 playlist = (*i)->region()->playlist();
4882 playlist->clear_changes ();
4883 playlist->duplicate (r, position, gap, times);
4884 _session->add_command(new StatefulDiffCommand (playlist));
4888 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4892 selection->set (foo);
4895 commit_reversible_command ();
4899 Editor::duplicate_selection (float times)
4901 if (selection->time.empty() || selection->tracks.empty()) {
4905 boost::shared_ptr<Playlist> playlist;
4907 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4909 bool in_command = false;
4911 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4912 if ((playlist = (*i)->playlist()) == 0) {
4915 playlist->clear_changes ();
4917 if (clicked_selection) {
4918 playlist->duplicate_range (selection->time[clicked_selection], times);
4920 playlist->duplicate_ranges (selection->time, times);
4924 begin_reversible_command (_("duplicate range selection"));
4927 _session->add_command (new StatefulDiffCommand (playlist));
4932 if (times == 1.0f) {
4933 // now "move" range selection to after the current range selection
4934 samplecnt_t distance = 0;
4936 if (clicked_selection) {
4938 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4940 distance = selection->time.end_sample () - selection->time.start ();
4943 selection->move_time (distance);
4945 commit_reversible_command ();
4949 /** Reset all selected points to the relevant default value */
4951 Editor::reset_point_selection ()
4953 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4954 ARDOUR::AutomationList::iterator j = (*i)->model ();
4955 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4960 Editor::center_playhead ()
4962 float const page = _visible_canvas_width * samples_per_pixel;
4963 center_screen_internal (playhead_cursor->current_sample (), page);
4967 Editor::center_edit_point ()
4969 float const page = _visible_canvas_width * samples_per_pixel;
4970 center_screen_internal (get_preferred_edit_position(), page);
4973 /** Caller must begin and commit a reversible command */
4975 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4977 playlist->clear_changes ();
4979 _session->add_command (new StatefulDiffCommand (playlist));
4983 Editor::nudge_track (bool use_edit, bool forwards)
4985 boost::shared_ptr<Playlist> playlist;
4986 samplepos_t distance;
4987 samplepos_t next_distance;
4991 start = get_preferred_edit_position();
4996 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5000 if (selection->tracks.empty()) {
5004 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5005 bool in_command = false;
5007 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5009 if ((playlist = (*i)->playlist()) == 0) {
5013 playlist->clear_changes ();
5014 playlist->clear_owned_changes ();
5016 playlist->nudge_after (start, distance, forwards);
5019 begin_reversible_command (_("nudge track"));
5022 vector<Command*> cmds;
5024 playlist->rdiff (cmds);
5025 _session->add_commands (cmds);
5027 _session->add_command (new StatefulDiffCommand (playlist));
5031 commit_reversible_command ();
5036 Editor::remove_last_capture ()
5038 vector<string> choices;
5045 if (Config->get_verify_remove_last_capture()) {
5046 prompt = _("Do you really want to destroy the last capture?"
5047 "\n(This is destructive and cannot be undone)");
5049 choices.push_back (_("No, do nothing."));
5050 choices.push_back (_("Yes, destroy it."));
5052 Choice prompter (_("Destroy last capture"), prompt, choices);
5054 if (prompter.run () == 1) {
5055 _session->remove_last_capture ();
5056 _regions->redisplay ();
5060 _session->remove_last_capture();
5061 _regions->redisplay ();
5066 Editor::normalize_region ()
5072 RegionSelection rs = get_regions_from_selection_and_entered ();
5078 NormalizeDialog dialog (rs.size() > 1);
5080 if (dialog.run () != RESPONSE_ACCEPT) {
5084 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5087 /* XXX: should really only count audio regions here */
5088 int const regions = rs.size ();
5090 /* Make a list of the selected audio regions' maximum amplitudes, and also
5091 obtain the maximum amplitude of them all.
5093 list<double> max_amps;
5094 list<double> rms_vals;
5097 bool use_rms = dialog.constrain_rms ();
5099 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5100 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5104 dialog.descend (1.0 / regions);
5105 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5107 double r = arv->audio_region()->rms (&dialog);
5108 max_rms = max (max_rms, r);
5109 rms_vals.push_back (r);
5113 /* the user cancelled the operation */
5117 max_amps.push_back (a);
5118 max_amp = max (max_amp, a);
5122 list<double>::const_iterator a = max_amps.begin ();
5123 list<double>::const_iterator l = rms_vals.begin ();
5124 bool in_command = false;
5126 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5127 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5132 arv->region()->clear_changes ();
5134 double amp = dialog.normalize_individually() ? *a : max_amp;
5135 double target = dialog.target_peak (); // dB
5138 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5139 const double t_rms = dialog.target_rms ();
5140 const gain_t c_peak = dB_to_coefficient (target);
5141 const gain_t c_rms = dB_to_coefficient (t_rms);
5142 if ((amp_rms / c_rms) > (amp / c_peak)) {
5148 arv->audio_region()->normalize (amp, target);
5151 begin_reversible_command (_("normalize"));
5154 _session->add_command (new StatefulDiffCommand (arv->region()));
5161 commit_reversible_command ();
5167 Editor::reset_region_scale_amplitude ()
5173 RegionSelection rs = get_regions_from_selection_and_entered ();
5179 bool in_command = false;
5181 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5182 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5185 arv->region()->clear_changes ();
5186 arv->audio_region()->set_scale_amplitude (1.0f);
5189 begin_reversible_command ("reset gain");
5192 _session->add_command (new StatefulDiffCommand (arv->region()));
5196 commit_reversible_command ();
5201 Editor::adjust_region_gain (bool up)
5203 RegionSelection rs = get_regions_from_selection_and_entered ();
5205 if (!_session || rs.empty()) {
5209 bool in_command = false;
5211 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5212 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5217 arv->region()->clear_changes ();
5219 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5227 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5230 begin_reversible_command ("adjust region gain");
5233 _session->add_command (new StatefulDiffCommand (arv->region()));
5237 commit_reversible_command ();
5242 Editor::reset_region_gain ()
5244 RegionSelection rs = get_regions_from_selection_and_entered ();
5246 if (!_session || rs.empty()) {
5250 bool in_command = false;
5252 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5253 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5258 arv->region()->clear_changes ();
5260 arv->audio_region()->set_scale_amplitude (1.0f);
5263 begin_reversible_command ("reset region gain");
5266 _session->add_command (new StatefulDiffCommand (arv->region()));
5270 commit_reversible_command ();
5275 Editor::reverse_region ()
5281 Reverse rev (*_session);
5282 apply_filter (rev, _("reverse regions"));
5286 Editor::strip_region_silence ()
5292 RegionSelection rs = get_regions_from_selection_and_entered ();
5298 std::list<RegionView*> audio_only;
5300 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5301 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5303 audio_only.push_back (arv);
5307 assert (!audio_only.empty());
5309 StripSilenceDialog d (_session, audio_only);
5310 int const r = d.run ();
5314 if (r == Gtk::RESPONSE_OK) {
5315 ARDOUR::AudioIntervalMap silences;
5316 d.silences (silences);
5317 StripSilence s (*_session, silences, d.fade_length());
5319 apply_filter (s, _("strip silence"), &d);
5324 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5326 Evoral::Sequence<Temporal::Beats>::Notes selected;
5327 mrv.selection_as_notelist (selected, true);
5329 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5330 v.push_back (selected);
5332 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5334 return op (mrv.midi_region()->model(), pos_beats, v);
5338 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5344 bool in_command = false;
5346 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5347 RegionSelection::const_iterator tmp = r;
5350 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5353 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5356 begin_reversible_command (op.name ());
5360 _session->add_command (cmd);
5368 commit_reversible_command ();
5369 _session->set_dirty ();
5374 Editor::fork_region ()
5376 RegionSelection rs = get_regions_from_selection_and_entered ();
5382 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5383 bool in_command = false;
5387 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5388 RegionSelection::iterator tmp = r;
5391 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5395 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5396 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5397 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5400 begin_reversible_command (_("Fork Region(s)"));
5403 playlist->clear_changes ();
5404 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5405 _session->add_command(new StatefulDiffCommand (playlist));
5407 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5415 commit_reversible_command ();
5420 Editor::quantize_region ()
5423 quantize_regions(get_regions_from_selection_and_entered ());
5428 Editor::quantize_regions (const RegionSelection& rs)
5430 if (rs.n_midi_regions() == 0) {
5434 if (!quantize_dialog) {
5435 quantize_dialog = new QuantizeDialog (*this);
5438 if (quantize_dialog->is_mapped()) {
5439 /* in progress already */
5443 quantize_dialog->present ();
5444 const int r = quantize_dialog->run ();
5445 quantize_dialog->hide ();
5447 if (r == Gtk::RESPONSE_OK) {
5448 Quantize quant (quantize_dialog->snap_start(),
5449 quantize_dialog->snap_end(),
5450 quantize_dialog->start_grid_size(),
5451 quantize_dialog->end_grid_size(),
5452 quantize_dialog->strength(),
5453 quantize_dialog->swing(),
5454 quantize_dialog->threshold());
5456 apply_midi_note_edit_op (quant, rs);
5461 Editor::legatize_region (bool shrink_only)
5464 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5469 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5471 if (rs.n_midi_regions() == 0) {
5475 Legatize legatize(shrink_only);
5476 apply_midi_note_edit_op (legatize, rs);
5480 Editor::transform_region ()
5483 transform_regions(get_regions_from_selection_and_entered ());
5488 Editor::transform_regions (const RegionSelection& rs)
5490 if (rs.n_midi_regions() == 0) {
5497 const int r = td.run();
5500 if (r == Gtk::RESPONSE_OK) {
5501 Transform transform(td.get());
5502 apply_midi_note_edit_op(transform, rs);
5507 Editor::transpose_region ()
5510 transpose_regions(get_regions_from_selection_and_entered ());
5515 Editor::transpose_regions (const RegionSelection& rs)
5517 if (rs.n_midi_regions() == 0) {
5522 int const r = d.run ();
5524 if (r == RESPONSE_ACCEPT) {
5525 Transpose transpose(d.semitones ());
5526 apply_midi_note_edit_op (transpose, rs);
5531 Editor::insert_patch_change (bool from_context)
5533 RegionSelection rs = get_regions_from_selection_and_entered ();
5539 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5541 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5542 there may be more than one, but the PatchChangeDialog can only offer
5543 one set of patch menus.
5545 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5547 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5548 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5550 if (d.run() == RESPONSE_CANCEL) {
5554 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5555 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5557 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5558 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5565 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5567 RegionSelection rs = get_regions_from_selection_and_entered ();
5573 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5574 bool in_command = false;
5579 int const N = rs.size ();
5581 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5582 RegionSelection::iterator tmp = r;
5585 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5587 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5590 progress->descend (1.0 / N);
5593 if (arv->audio_region()->apply (filter, progress) == 0) {
5595 playlist->clear_changes ();
5596 playlist->clear_owned_changes ();
5599 begin_reversible_command (command);
5603 if (filter.results.empty ()) {
5605 /* no regions returned; remove the old one */
5606 playlist->remove_region (arv->region ());
5610 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5612 /* first region replaces the old one */
5613 playlist->replace_region (arv->region(), *res, (*res)->position());
5617 while (res != filter.results.end()) {
5618 playlist->add_region (*res, (*res)->position());
5624 /* We might have removed regions, which alters other regions' layering_index,
5625 so we need to do a recursive diff here.
5627 vector<Command*> cmds;
5628 playlist->rdiff (cmds);
5629 _session->add_commands (cmds);
5631 _session->add_command(new StatefulDiffCommand (playlist));
5635 progress->ascend ();
5644 commit_reversible_command ();
5649 Editor::external_edit_region ()
5655 Editor::reset_region_gain_envelopes ()
5657 RegionSelection rs = get_regions_from_selection_and_entered ();
5659 if (!_session || rs.empty()) {
5663 bool in_command = false;
5665 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5666 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5668 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5669 XMLNode& before (alist->get_state());
5671 arv->audio_region()->set_default_envelope ();
5674 begin_reversible_command (_("reset region gain"));
5677 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5682 commit_reversible_command ();
5687 Editor::set_region_gain_visibility (RegionView* rv)
5689 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5691 arv->update_envelope_visibility();
5696 Editor::set_gain_envelope_visibility ()
5702 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5703 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5705 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5711 Editor::toggle_gain_envelope_active ()
5713 if (_ignore_region_action) {
5717 RegionSelection rs = get_regions_from_selection_and_entered ();
5719 if (!_session || rs.empty()) {
5723 bool in_command = false;
5725 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5726 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5728 arv->region()->clear_changes ();
5729 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5732 begin_reversible_command (_("region gain envelope active"));
5735 _session->add_command (new StatefulDiffCommand (arv->region()));
5740 commit_reversible_command ();
5745 Editor::toggle_region_lock ()
5747 if (_ignore_region_action) {
5751 RegionSelection rs = get_regions_from_selection_and_entered ();
5753 if (!_session || rs.empty()) {
5757 begin_reversible_command (_("toggle region lock"));
5759 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5760 (*i)->region()->clear_changes ();
5761 (*i)->region()->set_locked (!(*i)->region()->locked());
5762 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5765 commit_reversible_command ();
5769 Editor::toggle_region_video_lock ()
5771 if (_ignore_region_action) {
5775 RegionSelection rs = get_regions_from_selection_and_entered ();
5777 if (!_session || rs.empty()) {
5781 begin_reversible_command (_("Toggle Video Lock"));
5783 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5784 (*i)->region()->clear_changes ();
5785 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5786 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5789 commit_reversible_command ();
5793 Editor::toggle_region_lock_style ()
5795 if (_ignore_region_action) {
5799 RegionSelection rs = get_regions_from_selection_and_entered ();
5801 if (!_session || rs.empty()) {
5805 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5806 vector<Widget*> proxies = a->get_proxies();
5807 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5811 begin_reversible_command (_("toggle region lock style"));
5813 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5814 (*i)->region()->clear_changes ();
5815 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5816 (*i)->region()->set_position_lock_style (ns);
5817 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5820 commit_reversible_command ();
5824 Editor::toggle_opaque_region ()
5826 if (_ignore_region_action) {
5830 RegionSelection rs = get_regions_from_selection_and_entered ();
5832 if (!_session || rs.empty()) {
5836 begin_reversible_command (_("change region opacity"));
5838 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5839 (*i)->region()->clear_changes ();
5840 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5841 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5844 commit_reversible_command ();
5848 Editor::toggle_record_enable ()
5850 bool new_state = false;
5852 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5853 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5856 if (!rtav->is_track())
5860 new_state = !rtav->track()->rec_enable_control()->get_value();
5864 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5869 Editor::toggle_solo ()
5871 bool new_state = false;
5873 boost::shared_ptr<ControlList> cl (new ControlList);
5875 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5876 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5878 if (!stav || !stav->stripable()->solo_control()) {
5883 new_state = !stav->stripable()->solo_control()->soloed ();
5887 cl->push_back (stav->stripable()->solo_control());
5890 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5894 Editor::toggle_mute ()
5896 bool new_state = false;
5898 boost::shared_ptr<ControlList> cl (new ControlList);
5900 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5901 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5903 if (!stav || !stav->stripable()->mute_control()) {
5908 new_state = !stav->stripable()->mute_control()->muted();
5912 cl->push_back (stav->stripable()->mute_control());
5915 _session->set_controls (cl, new_state, Controllable::UseGroup);
5919 Editor::toggle_solo_isolate ()
5925 Editor::fade_range ()
5927 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5929 begin_reversible_command (_("fade range"));
5931 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5932 (*i)->fade_range (selection->time);
5935 commit_reversible_command ();
5940 Editor::set_fade_length (bool in)
5942 RegionSelection rs = get_regions_from_selection_and_entered ();
5948 /* we need a region to measure the offset from the start */
5950 RegionView* rv = rs.front ();
5952 samplepos_t pos = get_preferred_edit_position();
5956 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
5957 /* edit point is outside the relevant region */
5962 if (pos <= rv->region()->position()) {
5966 len = pos - rv->region()->position();
5967 cmd = _("set fade in length");
5969 if (pos >= rv->region()->last_sample()) {
5973 len = rv->region()->last_sample() - pos;
5974 cmd = _("set fade out length");
5977 bool in_command = false;
5979 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5980 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5986 boost::shared_ptr<AutomationList> alist;
5988 alist = tmp->audio_region()->fade_in();
5990 alist = tmp->audio_region()->fade_out();
5993 XMLNode &before = alist->get_state();
5996 tmp->audio_region()->set_fade_in_length (len);
5997 tmp->audio_region()->set_fade_in_active (true);
5999 tmp->audio_region()->set_fade_out_length (len);
6000 tmp->audio_region()->set_fade_out_active (true);
6004 begin_reversible_command (cmd);
6007 XMLNode &after = alist->get_state();
6008 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6012 commit_reversible_command ();
6017 Editor::set_fade_in_shape (FadeShape shape)
6019 RegionSelection rs = get_regions_from_selection_and_entered ();
6024 bool in_command = false;
6026 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6027 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6033 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6034 XMLNode &before = alist->get_state();
6036 tmp->audio_region()->set_fade_in_shape (shape);
6039 begin_reversible_command (_("set fade in shape"));
6042 XMLNode &after = alist->get_state();
6043 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6047 commit_reversible_command ();
6052 Editor::set_fade_out_shape (FadeShape shape)
6054 RegionSelection rs = get_regions_from_selection_and_entered ();
6059 bool in_command = false;
6061 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6062 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6068 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6069 XMLNode &before = alist->get_state();
6071 tmp->audio_region()->set_fade_out_shape (shape);
6074 begin_reversible_command (_("set fade out shape"));
6077 XMLNode &after = alist->get_state();
6078 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6082 commit_reversible_command ();
6087 Editor::set_fade_in_active (bool yn)
6089 RegionSelection rs = get_regions_from_selection_and_entered ();
6094 bool in_command = false;
6096 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6097 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6104 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6106 ar->clear_changes ();
6107 ar->set_fade_in_active (yn);
6110 begin_reversible_command (_("set fade in active"));
6113 _session->add_command (new StatefulDiffCommand (ar));
6117 commit_reversible_command ();
6122 Editor::set_fade_out_active (bool yn)
6124 RegionSelection rs = get_regions_from_selection_and_entered ();
6129 bool in_command = false;
6131 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6132 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6138 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6140 ar->clear_changes ();
6141 ar->set_fade_out_active (yn);
6144 begin_reversible_command (_("set fade out active"));
6147 _session->add_command(new StatefulDiffCommand (ar));
6151 commit_reversible_command ();
6156 Editor::toggle_region_fades (int dir)
6158 if (_ignore_region_action) {
6162 boost::shared_ptr<AudioRegion> ar;
6165 RegionSelection rs = get_regions_from_selection_and_entered ();
6171 RegionSelection::iterator i;
6172 for (i = rs.begin(); i != rs.end(); ++i) {
6173 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6175 yn = ar->fade_out_active ();
6177 yn = ar->fade_in_active ();
6183 if (i == rs.end()) {
6187 /* XXX should this undo-able? */
6188 bool in_command = false;
6190 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6191 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6194 ar->clear_changes ();
6196 if (dir == 1 || dir == 0) {
6197 ar->set_fade_in_active (!yn);
6200 if (dir == -1 || dir == 0) {
6201 ar->set_fade_out_active (!yn);
6204 begin_reversible_command (_("toggle fade active"));
6207 _session->add_command(new StatefulDiffCommand (ar));
6211 commit_reversible_command ();
6216 /** Update region fade visibility after its configuration has been changed */
6218 Editor::update_region_fade_visibility ()
6220 bool _fade_visibility = _session->config.get_show_region_fades ();
6222 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6223 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6225 if (_fade_visibility) {
6226 v->audio_view()->show_all_fades ();
6228 v->audio_view()->hide_all_fades ();
6235 Editor::set_edit_point ()
6238 MusicSample where (0, 0);
6240 if (!mouse_sample (where.sample, ignored)) {
6246 if (selection->markers.empty()) {
6248 mouse_add_new_marker (where.sample);
6253 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6256 loc->move_to (where.sample, where.division);
6262 Editor::set_playhead_cursor ()
6264 if (entered_marker) {
6265 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6267 MusicSample where (0, 0);
6270 if (!mouse_sample (where.sample, ignored)) {
6277 _session->request_locate (where.sample, _session->transport_rolling());
6281 //not sure what this was for; remove it for now.
6282 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6283 // cancel_time_selection();
6289 Editor::split_region ()
6291 if (_drags->active ()) {
6295 //if a range is selected, separate it
6296 if (!selection->time.empty()) {
6297 separate_regions_between (selection->time);
6301 //if no range was selected, try to find some regions to split
6302 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6304 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6305 const samplepos_t pos = get_preferred_edit_position();
6306 const int32_t division = get_grid_music_divisions (0);
6307 MusicSample where (pos, division);
6313 split_regions_at (where, rs);
6319 Editor::select_next_stripable (bool routes_only)
6321 if (selection->tracks.empty()) {
6322 selection->set (track_views.front());
6326 TimeAxisView* current = selection->tracks.front();
6330 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6332 if (*i == current) {
6334 if (i != track_views.end()) {
6337 current = (*(track_views.begin()));
6338 //selection->set (*(track_views.begin()));
6345 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6346 valid = rui && rui->route()->active();
6348 valid = 0 != current->stripable ().get();
6351 } while (current->hidden() || !valid);
6353 selection->set (current);
6355 ensure_time_axis_view_is_visible (*current, false);
6359 Editor::select_prev_stripable (bool routes_only)
6361 if (selection->tracks.empty()) {
6362 selection->set (track_views.front());
6366 TimeAxisView* current = selection->tracks.front();
6370 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6372 if (*i == current) {
6374 if (i != track_views.rend()) {
6377 current = *(track_views.rbegin());
6383 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6384 valid = rui && rui->route()->active();
6386 valid = 0 != current->stripable ().get();
6389 } while (current->hidden() || !valid);
6391 selection->set (current);
6393 ensure_time_axis_view_is_visible (*current, false);
6397 Editor::set_loop_from_selection (bool play)
6399 if (_session == 0) {
6403 samplepos_t start, end;
6404 if (!get_selection_extents (start, end))
6407 set_loop_range (start, end, _("set loop range from selection"));
6410 _session->request_play_loop (true, true);
6415 Editor::set_loop_from_region (bool play)
6417 samplepos_t start, end;
6418 if (!get_selection_extents (start, end))
6421 set_loop_range (start, end, _("set loop range from region"));
6424 _session->request_locate (start, true);
6425 _session->request_play_loop (true);
6430 Editor::set_punch_from_selection ()
6432 if (_session == 0) {
6436 samplepos_t start, end;
6437 if (!get_selection_extents (start, end))
6440 set_punch_range (start, end, _("set punch range from selection"));
6444 Editor::set_auto_punch_range ()
6446 // auto punch in/out button from a single button
6447 // If Punch In is unset, set punch range from playhead to end, enable punch in
6448 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6449 // rewound beyond the Punch In marker, in which case that marker will be moved back
6450 // to the current playhead position.
6451 // If punch out is set, it clears the punch range and Punch In/Out buttons
6453 if (_session == 0) {
6457 Location* tpl = transport_punch_location();
6458 samplepos_t now = playhead_cursor->current_sample();
6459 samplepos_t begin = now;
6460 samplepos_t end = _session->current_end_sample();
6462 if (!_session->config.get_punch_in()) {
6463 // First Press - set punch in and create range from here to eternity
6464 set_punch_range (begin, end, _("Auto Punch In"));
6465 _session->config.set_punch_in(true);
6466 } else if (tpl && !_session->config.get_punch_out()) {
6467 // Second press - update end range marker and set punch_out
6468 if (now < tpl->start()) {
6469 // playhead has been rewound - move start back and pretend nothing happened
6471 set_punch_range (begin, end, _("Auto Punch In/Out"));
6473 // normal case for 2nd press - set the punch out
6474 end = playhead_cursor->current_sample ();
6475 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6476 _session->config.set_punch_out(true);
6479 if (_session->config.get_punch_out()) {
6480 _session->config.set_punch_out(false);
6483 if (_session->config.get_punch_in()) {
6484 _session->config.set_punch_in(false);
6489 // third press - unset punch in/out and remove range
6490 _session->locations()->remove(tpl);
6497 Editor::set_session_extents_from_selection ()
6499 if (_session == 0) {
6503 samplepos_t start, end;
6504 if (!get_selection_extents (start, end))
6508 if ((loc = _session->locations()->session_range_location()) == 0) {
6509 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6511 XMLNode &before = loc->get_state();
6513 _session->set_session_extents (start, end);
6515 XMLNode &after = loc->get_state();
6517 begin_reversible_command (_("set session start/end from selection"));
6519 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6521 commit_reversible_command ();
6524 _session->set_end_is_free (false);
6528 Editor::set_punch_start_from_edit_point ()
6532 MusicSample start (0, 0);
6533 samplepos_t end = max_samplepos;
6535 //use the existing punch end, if any
6536 Location* tpl = transport_punch_location();
6541 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6542 start.sample = _session->audible_sample();
6544 start.sample = get_preferred_edit_position();
6547 //snap the selection start/end
6550 //if there's not already a sensible selection endpoint, go "forever"
6551 if (start.sample > end ) {
6552 end = max_samplepos;
6555 set_punch_range (start.sample, end, _("set punch start from EP"));
6561 Editor::set_punch_end_from_edit_point ()
6565 samplepos_t start = 0;
6566 MusicSample end (max_samplepos, 0);
6568 //use the existing punch start, if any
6569 Location* tpl = transport_punch_location();
6571 start = tpl->start();
6574 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6575 end.sample = _session->audible_sample();
6577 end.sample = get_preferred_edit_position();
6580 //snap the selection start/end
6583 set_punch_range (start, end.sample, _("set punch end from EP"));
6589 Editor::set_loop_start_from_edit_point ()
6593 MusicSample start (0, 0);
6594 samplepos_t end = max_samplepos;
6596 //use the existing loop end, if any
6597 Location* tpl = transport_loop_location();
6602 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6603 start.sample = _session->audible_sample();
6605 start.sample = get_preferred_edit_position();
6608 //snap the selection start/end
6611 //if there's not already a sensible selection endpoint, go "forever"
6612 if (start.sample > end ) {
6613 end = max_samplepos;
6616 set_loop_range (start.sample, end, _("set loop start from EP"));
6622 Editor::set_loop_end_from_edit_point ()
6626 samplepos_t start = 0;
6627 MusicSample end (max_samplepos, 0);
6629 //use the existing loop start, if any
6630 Location* tpl = transport_loop_location();
6632 start = tpl->start();
6635 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6636 end.sample = _session->audible_sample();
6638 end.sample = get_preferred_edit_position();
6641 //snap the selection start/end
6644 set_loop_range (start, end.sample, _("set loop end from EP"));
6649 Editor::set_punch_from_region ()
6651 samplepos_t start, end;
6652 if (!get_selection_extents (start, end))
6655 set_punch_range (start, end, _("set punch range from region"));
6659 Editor::pitch_shift_region ()
6661 RegionSelection rs = get_regions_from_selection_and_entered ();
6663 RegionSelection audio_rs;
6664 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6665 if (dynamic_cast<AudioRegionView*> (*i)) {
6666 audio_rs.push_back (*i);
6670 if (audio_rs.empty()) {
6674 pitch_shift (audio_rs, 1.2);
6678 Editor::set_tempo_from_region ()
6680 RegionSelection rs = get_regions_from_selection_and_entered ();
6682 if (!_session || rs.empty()) {
6686 RegionView* rv = rs.front();
6688 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6692 Editor::use_range_as_bar ()
6694 samplepos_t start, end;
6695 if (get_edit_op_range (start, end)) {
6696 define_one_bar (start, end);
6701 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6703 samplepos_t length = end - start;
6705 const Meter& m (_session->tempo_map().meter_at_sample (start));
6707 /* length = 1 bar */
6709 /* We're going to deliver a constant tempo here,
6710 so we can use samples per beat to determine length.
6711 now we want samples per beat.
6712 we have samples per bar, and beats per bar, so ...
6715 /* XXXX METER MATH */
6717 double samples_per_beat = length / m.divisions_per_bar();
6719 /* beats per minute = */
6721 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6723 /* now decide whether to:
6725 (a) set global tempo
6726 (b) add a new tempo marker
6730 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6732 bool do_global = false;
6734 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6736 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6737 at the start, or create a new marker
6740 vector<string> options;
6741 options.push_back (_("Cancel"));
6742 options.push_back (_("Add new marker"));
6743 options.push_back (_("Set global tempo"));
6746 _("Define one bar"),
6747 _("Do you want to set the global tempo or add a new tempo marker?"),
6751 c.set_default_response (2);
6767 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6768 if the marker is at the region starter, change it, otherwise add
6773 begin_reversible_command (_("set tempo from region"));
6774 XMLNode& before (_session->tempo_map().get_state());
6777 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6778 } else if (t.sample() == start) {
6779 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6781 /* constant tempo */
6782 const Tempo tempo (beats_per_minute, t.note_type());
6783 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6786 XMLNode& after (_session->tempo_map().get_state());
6788 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6789 commit_reversible_command ();
6793 Editor::split_region_at_transients ()
6795 AnalysisFeatureList positions;
6797 RegionSelection rs = get_regions_from_selection_and_entered ();
6799 if (!_session || rs.empty()) {
6803 begin_reversible_command (_("split regions"));
6805 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6807 RegionSelection::iterator tmp;
6812 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6815 ar->transients (positions);
6816 split_region_at_points ((*i)->region(), positions, true);
6823 commit_reversible_command ();
6828 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6830 bool use_rhythmic_rodent = false;
6832 boost::shared_ptr<Playlist> pl = r->playlist();
6834 list<boost::shared_ptr<Region> > new_regions;
6840 if (positions.empty()) {
6844 if (positions.size() > 20 && can_ferret) {
6845 std::string msgstr = string_compose (_("You are about to split\n%1\ninto %2 pieces.\nThis could take a long time."), r->name(), positions.size() + 1);
6846 MessageDialog msg (msgstr,
6849 Gtk::BUTTONS_OK_CANCEL);
6852 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6853 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6855 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6858 msg.set_title (_("Excessive split?"));
6861 int response = msg.run();
6867 case RESPONSE_APPLY:
6868 use_rhythmic_rodent = true;
6875 if (use_rhythmic_rodent) {
6876 show_rhythm_ferret ();
6880 AnalysisFeatureList::const_iterator x;
6882 pl->clear_changes ();
6883 pl->clear_owned_changes ();
6885 x = positions.begin();
6887 if (x == positions.end()) {
6892 pl->remove_region (r);
6894 samplepos_t pos = 0;
6896 samplepos_t rstart = r->first_sample ();
6897 samplepos_t rend = r->last_sample ();
6899 while (x != positions.end()) {
6901 /* deal with positons that are out of scope of present region bounds */
6902 if (*x <= rstart || *x > rend) {
6907 /* file start = original start + how far we from the initial position ? */
6909 samplepos_t file_start = r->start() + pos;
6911 /* length = next position - current position */
6913 samplepos_t len = (*x) - pos - rstart;
6915 /* XXX we do we really want to allow even single-sample regions?
6916 * shouldn't we have some kind of lower limit on region size?
6925 if (RegionFactory::region_name (new_name, r->name())) {
6929 /* do NOT announce new regions 1 by one, just wait till they are all done */
6933 plist.add (ARDOUR::Properties::start, file_start);
6934 plist.add (ARDOUR::Properties::length, len);
6935 plist.add (ARDOUR::Properties::name, new_name);
6936 plist.add (ARDOUR::Properties::layer, 0);
6937 // TODO set transients_offset
6939 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6940 /* because we set annouce to false, manually add the new region to the
6943 RegionFactory::map_add (nr);
6945 pl->add_region (nr, rstart + pos);
6948 new_regions.push_front(nr);
6957 RegionFactory::region_name (new_name, r->name());
6959 /* Add the final region */
6962 plist.add (ARDOUR::Properties::start, r->start() + pos);
6963 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6964 plist.add (ARDOUR::Properties::name, new_name);
6965 plist.add (ARDOUR::Properties::layer, 0);
6967 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6968 /* because we set annouce to false, manually add the new region to the
6971 RegionFactory::map_add (nr);
6972 pl->add_region (nr, r->position() + pos);
6975 new_regions.push_front(nr);
6980 /* We might have removed regions, which alters other regions' layering_index,
6981 so we need to do a recursive diff here.
6983 vector<Command*> cmds;
6985 _session->add_commands (cmds);
6987 _session->add_command (new StatefulDiffCommand (pl));
6991 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6992 set_selected_regionview_from_region_list ((*i), Selection::Add);
6998 Editor::place_transient()
7004 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7010 samplepos_t where = get_preferred_edit_position();
7012 begin_reversible_command (_("place transient"));
7014 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7015 (*r)->region()->add_transient(where);
7018 commit_reversible_command ();
7022 Editor::remove_transient(ArdourCanvas::Item* item)
7028 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7031 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7032 _arv->remove_transient (*(float*) _line->get_data ("position"));
7036 Editor::snap_regions_to_grid ()
7038 list <boost::shared_ptr<Playlist > > used_playlists;
7040 RegionSelection rs = get_regions_from_selection_and_entered ();
7042 if (!_session || rs.empty()) {
7046 begin_reversible_command (_("snap regions to grid"));
7048 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7050 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7052 if (!pl->frozen()) {
7053 /* we haven't seen this playlist before */
7055 /* remember used playlists so we can thaw them later */
7056 used_playlists.push_back(pl);
7059 (*r)->region()->clear_changes ();
7061 MusicSample start ((*r)->region()->first_sample (), 0);
7062 snap_to (start, RoundNearest, SnapToGrid );
7063 (*r)->region()->set_position (start.sample, start.division);
7064 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7067 while (used_playlists.size() > 0) {
7068 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7070 used_playlists.pop_front();
7073 commit_reversible_command ();
7077 Editor::close_region_gaps ()
7079 list <boost::shared_ptr<Playlist > > used_playlists;
7081 RegionSelection rs = get_regions_from_selection_and_entered ();
7083 if (!_session || rs.empty()) {
7087 Dialog dialog (_("Close Region Gaps"));
7090 table.set_spacings (12);
7091 table.set_border_width (12);
7092 Label* l = manage (left_aligned_label (_("Crossfade length")));
7093 table.attach (*l, 0, 1, 0, 1);
7095 SpinButton spin_crossfade (1, 0);
7096 spin_crossfade.set_range (0, 15);
7097 spin_crossfade.set_increments (1, 1);
7098 spin_crossfade.set_value (5);
7099 table.attach (spin_crossfade, 1, 2, 0, 1);
7101 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7103 l = manage (left_aligned_label (_("Pull-back length")));
7104 table.attach (*l, 0, 1, 1, 2);
7106 SpinButton spin_pullback (1, 0);
7107 spin_pullback.set_range (0, 100);
7108 spin_pullback.set_increments (1, 1);
7109 spin_pullback.set_value(30);
7110 table.attach (spin_pullback, 1, 2, 1, 2);
7112 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7114 dialog.get_vbox()->pack_start (table);
7115 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7116 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7119 if (dialog.run () == RESPONSE_CANCEL) {
7123 samplepos_t crossfade_len = spin_crossfade.get_value();
7124 samplepos_t pull_back_samples = spin_pullback.get_value();
7126 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7127 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7129 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7131 begin_reversible_command (_("close region gaps"));
7134 boost::shared_ptr<Region> last_region;
7136 rs.sort_by_position_and_track();
7138 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7140 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7142 if (!pl->frozen()) {
7143 /* we haven't seen this playlist before */
7145 /* remember used playlists so we can thaw them later */
7146 used_playlists.push_back(pl);
7150 samplepos_t position = (*r)->region()->position();
7152 if (idx == 0 || position < last_region->position()){
7153 last_region = (*r)->region();
7158 (*r)->region()->clear_changes ();
7159 (*r)->region()->trim_front((position - pull_back_samples));
7161 last_region->clear_changes ();
7162 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7164 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7165 _session->add_command (new StatefulDiffCommand (last_region));
7167 last_region = (*r)->region();
7171 while (used_playlists.size() > 0) {
7172 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7174 used_playlists.pop_front();
7177 commit_reversible_command ();
7181 Editor::tab_to_transient (bool forward)
7183 AnalysisFeatureList positions;
7185 RegionSelection rs = get_regions_from_selection_and_entered ();
7191 samplepos_t pos = _session->audible_sample ();
7193 if (!selection->tracks.empty()) {
7195 /* don't waste time searching for transients in duplicate playlists.
7198 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7200 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7202 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7205 boost::shared_ptr<Track> tr = rtv->track();
7207 boost::shared_ptr<Playlist> pl = tr->playlist ();
7209 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7212 positions.push_back (result);
7225 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7226 (*r)->region()->get_transients (positions);
7230 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7233 AnalysisFeatureList::iterator x;
7235 for (x = positions.begin(); x != positions.end(); ++x) {
7241 if (x != positions.end ()) {
7242 _session->request_locate (*x);
7246 AnalysisFeatureList::reverse_iterator x;
7248 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7254 if (x != positions.rend ()) {
7255 _session->request_locate (*x);
7261 Editor::playhead_forward_to_grid ()
7267 MusicSample pos (playhead_cursor->current_sample (), 0);
7269 if (pos.sample < max_samplepos - 1) {
7271 snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
7272 _session->request_locate (pos.sample);
7278 Editor::playhead_backward_to_grid ()
7284 MusicSample pos (playhead_cursor->current_sample (), 0);
7286 if (pos.sample > 2) {
7288 snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
7289 _session->request_locate (pos.sample);
7294 Editor::set_track_height (Height h)
7296 TrackSelection& ts (selection->tracks);
7298 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7299 (*x)->set_height_enum (h);
7304 Editor::toggle_tracks_active ()
7306 TrackSelection& ts (selection->tracks);
7308 bool target = false;
7314 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7315 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7319 target = !rtv->_route->active();
7322 rtv->_route->set_active (target, this);
7328 Editor::remove_tracks ()
7330 /* this will delete GUI objects that may be the subject of an event
7331 handler in which this method is called. Defer actual deletion to the
7332 next idle callback, when all event handling is finished.
7334 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7338 Editor::idle_remove_tracks ()
7340 Session::StateProtector sp (_session);
7342 return false; /* do not call again */
7346 Editor::_remove_tracks ()
7348 TrackSelection& ts (selection->tracks);
7354 vector<string> choices;
7359 const char* trackstr;
7362 vector<boost::shared_ptr<Route> > routes;
7363 vector<boost::shared_ptr<VCA> > vcas;
7364 bool special_bus = false;
7366 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7367 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7369 vcas.push_back (vtv->vca());
7373 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7377 if (rtv->is_track()) {
7382 routes.push_back (rtv->_route);
7384 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7389 if (special_bus && !Config->get_allow_special_bus_removal()) {
7390 MessageDialog msg (_("That would be bad news ...."),
7394 msg.set_secondary_text (string_compose (_(
7395 "Removing the master or monitor bus is such a bad idea\n\
7396 that %1 is not going to allow it.\n\
7398 If you really want to do this sort of thing\n\
7399 edit your ardour.rc file to set the\n\
7400 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7407 if (ntracks + nbusses + nvcas == 0) {
7413 trackstr = P_("track", "tracks", ntracks);
7414 busstr = P_("bus", "busses", nbusses);
7415 vcastr = P_("VCA", "VCAs", nvcas);
7417 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7418 title = _("Remove various strips");
7419 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7420 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7422 else if (ntracks > 0 && nbusses > 0) {
7423 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7424 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7425 ntracks, trackstr, nbusses, busstr);
7427 else if (ntracks > 0 && nvcas > 0) {
7428 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7429 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7430 ntracks, trackstr, nvcas, vcastr);
7432 else if (nbusses > 0 && nvcas > 0) {
7433 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7434 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7435 nbusses, busstr, nvcas, vcastr);
7437 else if (ntracks > 0) {
7438 title = string_compose (_("Remove %1"), trackstr);
7439 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7442 else if (nbusses > 0) {
7443 title = string_compose (_("Remove %1"), busstr);
7444 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7447 else if (nvcas > 0) {
7448 title = string_compose (_("Remove %1"), vcastr);
7449 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7457 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7460 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7462 choices.push_back (_("No, do nothing."));
7463 if (ntracks + nbusses + nvcas > 1) {
7464 choices.push_back (_("Yes, remove them."));
7466 choices.push_back (_("Yes, remove it."));
7469 Choice prompter (title, prompt, choices);
7471 if (prompter.run () != 1) {
7475 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7476 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7477 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7478 * likely because deletion requires selection) this will call
7479 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7480 * It's likewise likely that the route that has just been displayed in the
7481 * Editor-Mixer will be next in line for deletion.
7483 * So simply switch to the master-bus (if present)
7485 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7486 if ((*i)->stripable ()->is_master ()) {
7487 set_selected_mixer_strip (*(*i));
7494 PresentationInfo::ChangeSuspender cs;
7495 DisplaySuspender ds;
7497 boost::shared_ptr<RouteList> rl (new RouteList);
7498 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7501 _session->remove_routes (rl);
7503 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7504 _session->vca_manager().remove_vca (*x);
7508 /* TrackSelection and RouteList leave scope,
7509 * destructors are called,
7510 * diskstream drops references, save_state is called (again for every track)
7515 Editor::do_insert_time ()
7517 if (selection->tracks.empty()) {
7521 InsertRemoveTimeDialog d (*this);
7522 int response = d.run ();
7524 if (response != RESPONSE_OK) {
7528 if (d.distance() == 0) {
7535 d.intersected_region_action (),
7539 d.move_glued_markers(),
7540 d.move_locked_markers(),
7546 Editor::insert_time (
7547 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7548 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7552 if (Config->get_edit_mode() == Lock) {
7555 bool in_command = false;
7557 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7559 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7563 /* don't operate on any playlist more than once, which could
7564 * happen if "all playlists" is enabled, but there is more
7565 * than 1 track using playlists "from" a given track.
7568 set<boost::shared_ptr<Playlist> > pl;
7570 if (all_playlists) {
7571 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7572 if (rtav && rtav->track ()) {
7573 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7574 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7579 if ((*x)->playlist ()) {
7580 pl.insert ((*x)->playlist ());
7584 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7586 (*i)->clear_changes ();
7587 (*i)->clear_owned_changes ();
7590 begin_reversible_command (_("insert time"));
7594 if (opt == SplitIntersected) {
7595 /* non musical split */
7596 (*i)->split (MusicSample (pos, 0));
7599 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7601 vector<Command*> cmds;
7603 _session->add_commands (cmds);
7605 _session->add_command (new StatefulDiffCommand (*i));
7609 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7612 begin_reversible_command (_("insert time"));
7615 rtav->route ()->shift (pos, samples);
7622 const int32_t divisions = get_grid_music_divisions (0);
7623 XMLNode& before (_session->locations()->get_state());
7624 Locations::LocationList copy (_session->locations()->list());
7626 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7628 Locations::LocationList::const_iterator tmp;
7630 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7631 bool const was_locked = (*i)->locked ();
7632 if (locked_markers_too) {
7636 if ((*i)->start() >= pos) {
7637 // move end first, in case we're moving by more than the length of the range
7638 if (!(*i)->is_mark()) {
7639 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7641 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7653 begin_reversible_command (_("insert time"));
7656 XMLNode& after (_session->locations()->get_state());
7657 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7663 begin_reversible_command (_("insert time"));
7666 XMLNode& before (_session->tempo_map().get_state());
7667 _session->tempo_map().insert_time (pos, samples);
7668 XMLNode& after (_session->tempo_map().get_state());
7669 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7673 commit_reversible_command ();
7678 Editor::do_remove_time ()
7680 if (selection->tracks.empty()) {
7684 InsertRemoveTimeDialog d (*this, true);
7686 int response = d.run ();
7688 if (response != RESPONSE_OK) {
7692 samplecnt_t distance = d.distance();
7694 if (distance == 0) {
7704 d.move_glued_markers(),
7705 d.move_locked_markers(),
7711 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7712 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7714 if (Config->get_edit_mode() == Lock) {
7715 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7718 bool in_command = false;
7720 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7722 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7726 XMLNode &before = pl->get_state();
7729 begin_reversible_command (_("remove time"));
7733 std::list<AudioRange> rl;
7734 AudioRange ar(pos, pos+samples, 0);
7737 pl->shift (pos, -samples, true, ignore_music_glue);
7739 XMLNode &after = pl->get_state();
7741 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7745 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7748 begin_reversible_command (_("remove time"));
7751 rtav->route ()->shift (pos, -samples);
7755 const int32_t divisions = get_grid_music_divisions (0);
7756 std::list<Location*> loc_kill_list;
7761 XMLNode& before (_session->locations()->get_state());
7762 Locations::LocationList copy (_session->locations()->list());
7764 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7765 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7767 bool const was_locked = (*i)->locked ();
7768 if (locked_markers_too) {
7772 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7773 if ((*i)->end() >= pos
7774 && (*i)->end() < pos+samples
7775 && (*i)->start() >= pos
7776 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7778 loc_kill_list.push_back(*i);
7779 } else { // only start or end is included, try to do the right thing
7780 // move start before moving end, to avoid trying to move the end to before the start
7781 // if we're removing more time than the length of the range
7782 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7783 // start is within cut
7784 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7786 } else if ((*i)->start() >= pos+samples) {
7787 // start (and thus entire range) lies beyond end of cut
7788 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7791 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7792 // end is inside cut
7793 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7795 } else if ((*i)->end() >= pos+samples) {
7796 // end is beyond end of cut
7797 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7802 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7803 loc_kill_list.push_back(*i);
7805 } else if ((*i)->start() >= pos) {
7806 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7816 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7817 _session->locations()->remove (*i);
7822 begin_reversible_command (_("remove time"));
7825 XMLNode& after (_session->locations()->get_state());
7826 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7831 XMLNode& before (_session->tempo_map().get_state());
7833 if (_session->tempo_map().remove_time (pos, samples)) {
7835 begin_reversible_command (_("remove time"));
7838 XMLNode& after (_session->tempo_map().get_state());
7839 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7844 commit_reversible_command ();
7849 Editor::fit_selection ()
7851 if (!selection->tracks.empty()) {
7852 fit_tracks (selection->tracks);
7856 /* no selected tracks - use tracks with selected regions */
7858 if (!selection->regions.empty()) {
7859 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7860 tvl.push_back (&(*r)->get_time_axis_view ());
7866 } else if (internal_editing()) {
7867 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7870 if (entered_track) {
7871 tvl.push_back (entered_track);
7879 Editor::fit_tracks (TrackViewList & tracks)
7881 if (tracks.empty()) {
7885 uint32_t child_heights = 0;
7886 int visible_tracks = 0;
7888 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7890 if (!(*t)->marked_for_display()) {
7894 child_heights += (*t)->effective_height() - (*t)->current_height();
7898 /* compute the per-track height from:
7900 * total canvas visible height
7901 * - height that will be taken by visible children of selected tracks
7902 * - height of the ruler/hscroll area
7904 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7905 double first_y_pos = DBL_MAX;
7907 if (h < TimeAxisView::preset_height (HeightSmall)) {
7908 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7909 /* too small to be displayed */
7913 undo_visual_stack.push_back (current_visual_state (true));
7914 PBD::Unwinder<bool> nsv (no_save_visual, true);
7916 /* build a list of all tracks, including children */
7919 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7921 TimeAxisView::Children c = (*i)->get_child_list ();
7922 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7923 all.push_back (j->get());
7928 // find selection range.
7929 // if someone knows how to user TrackViewList::iterator for this
7931 int selected_top = -1;
7932 int selected_bottom = -1;
7934 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7935 if ((*t)->marked_for_display ()) {
7936 if (tracks.contains(*t)) {
7937 if (selected_top == -1) {
7940 selected_bottom = i;
7946 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7947 if ((*t)->marked_for_display ()) {
7948 if (tracks.contains(*t)) {
7949 (*t)->set_height (h);
7950 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7952 if (i > selected_top && i < selected_bottom) {
7953 hide_track_in_display (*t);
7960 set the controls_layout height now, because waiting for its size
7961 request signal handler will cause the vertical adjustment setting to fail
7964 controls_layout.property_height () = _full_canvas_height;
7965 vertical_adjustment.set_value (first_y_pos);
7967 redo_visual_stack.push_back (current_visual_state (true));
7969 visible_tracks_selector.set_text (_("Sel"));
7973 Editor::save_visual_state (uint32_t n)
7975 while (visual_states.size() <= n) {
7976 visual_states.push_back (0);
7979 if (visual_states[n] != 0) {
7980 delete visual_states[n];
7983 visual_states[n] = current_visual_state (true);
7988 Editor::goto_visual_state (uint32_t n)
7990 if (visual_states.size() <= n) {
7994 if (visual_states[n] == 0) {
7998 use_visual_state (*visual_states[n]);
8002 Editor::start_visual_state_op (uint32_t n)
8004 save_visual_state (n);
8006 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8008 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8009 pup->set_text (buf);
8014 Editor::cancel_visual_state_op (uint32_t n)
8016 goto_visual_state (n);
8020 Editor::toggle_region_mute ()
8022 if (_ignore_region_action) {
8026 RegionSelection rs = get_regions_from_selection_and_entered ();
8032 if (rs.size() > 1) {
8033 begin_reversible_command (_("mute regions"));
8035 begin_reversible_command (_("mute region"));
8038 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8040 (*i)->region()->playlist()->clear_changes ();
8041 (*i)->region()->set_muted (!(*i)->region()->muted ());
8042 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8046 commit_reversible_command ();
8050 Editor::combine_regions ()
8052 /* foreach track with selected regions, take all selected regions
8053 and join them into a new region containing the subregions (as a
8057 typedef set<RouteTimeAxisView*> RTVS;
8060 if (selection->regions.empty()) {
8064 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8065 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8068 tracks.insert (rtv);
8072 begin_reversible_command (_("combine regions"));
8074 vector<RegionView*> new_selection;
8076 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8079 if ((rv = (*i)->combine_regions ()) != 0) {
8080 new_selection.push_back (rv);
8084 selection->clear_regions ();
8085 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8086 selection->add (*i);
8089 commit_reversible_command ();
8093 Editor::uncombine_regions ()
8095 typedef set<RouteTimeAxisView*> RTVS;
8098 if (selection->regions.empty()) {
8102 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8103 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8106 tracks.insert (rtv);
8110 begin_reversible_command (_("uncombine regions"));
8112 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8113 (*i)->uncombine_regions ();
8116 commit_reversible_command ();
8120 Editor::toggle_midi_input_active (bool flip_others)
8123 boost::shared_ptr<RouteList> rl (new RouteList);
8125 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8126 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8132 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8135 rl->push_back (rtav->route());
8136 onoff = !mt->input_active();
8140 _session->set_exclusive_input_active (rl, onoff, flip_others);
8143 static bool ok_fine (GdkEventAny*) { return true; }
8149 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8151 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8152 lock_dialog->get_vbox()->pack_start (*padlock);
8153 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8155 ArdourButton* b = manage (new ArdourButton);
8156 b->set_name ("lock button");
8157 b->set_text (_("Click to unlock"));
8158 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8159 lock_dialog->get_vbox()->pack_start (*b);
8161 lock_dialog->get_vbox()->show_all ();
8162 lock_dialog->set_size_request (200, 200);
8165 delete _main_menu_disabler;
8166 _main_menu_disabler = new MainMenuDisabler;
8168 lock_dialog->present ();
8170 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8176 lock_dialog->hide ();
8178 delete _main_menu_disabler;
8179 _main_menu_disabler = 0;
8181 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8182 start_lock_event_timing ();
8187 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8189 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8193 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8195 Timers::TimerSuspender t;
8196 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8197 Gtkmm2ext::UI::instance()->flush_pending (1);
8201 Editor::bring_all_sources_into_session ()
8208 ArdourDialog w (_("Moving embedded files into session folder"));
8209 w.get_vbox()->pack_start (msg);
8212 /* flush all pending GUI events because we're about to start copying
8216 Timers::TimerSuspender t;
8217 Gtkmm2ext::UI::instance()->flush_pending (3);
8221 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));