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) {
197 case SnapToRegionStart:
198 case SnapToRegionSync:
199 case SnapToRegionEnd:
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 vector<RegionPoint> interesting_points;
712 boost::shared_ptr<Region> r;
713 TrackViewList tracks;
716 region_boundary_cache.clear ();
722 bool maybe_first_sample = false;
724 switch (_snap_type) {
725 case SnapToRegionStart:
726 interesting_points.push_back (Start);
727 maybe_first_sample = true;
729 case SnapToRegionEnd:
730 interesting_points.push_back (End);
732 case SnapToRegionSync:
733 interesting_points.push_back (SyncPoint);
735 case SnapToRegionBoundary:
736 interesting_points.push_back (Start);
737 interesting_points.push_back (End);
738 maybe_first_sample = true;
741 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
742 abort(); /*NOTREACHED*/
746 TimeAxisView *ontrack = 0;
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());
827 boost::shared_ptr<Region>
828 Editor::find_next_region (samplepos_t sample, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
830 TrackViewList::iterator i;
831 samplepos_t closest = max_samplepos;
832 boost::shared_ptr<Region> ret;
833 samplepos_t rpos = 0;
835 samplepos_t track_sample;
837 for (i = tracks.begin(); i != tracks.end(); ++i) {
839 samplecnt_t distance;
840 boost::shared_ptr<Region> r;
842 track_sample = sample;
844 if ((r = (*i)->find_next_region (track_sample, point, dir)) == 0) {
850 rpos = r->first_sample ();
854 rpos = r->last_sample ();
858 rpos = r->sync_position ();
863 distance = rpos - sample;
865 distance = sample - rpos;
868 if (distance < closest) {
880 Editor::find_next_region_boundary (samplepos_t pos, int32_t dir, const TrackViewList& tracks)
882 samplecnt_t distance = max_samplepos;
883 samplepos_t current_nearest = -1;
885 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
886 samplepos_t contender;
889 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
895 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
899 d = ::llabs (pos - contender);
902 current_nearest = contender;
907 return current_nearest;
911 Editor::get_region_boundary (samplepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
916 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
918 if (!selection->tracks.empty()) {
920 target = find_next_region_boundary (pos, dir, selection->tracks);
924 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
925 get_onscreen_tracks (tvl);
926 target = find_next_region_boundary (pos, dir, tvl);
928 target = find_next_region_boundary (pos, dir, track_views);
934 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
935 get_onscreen_tracks (tvl);
936 target = find_next_region_boundary (pos, dir, tvl);
938 target = find_next_region_boundary (pos, dir, track_views);
946 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
948 samplepos_t pos = playhead_cursor->current_sample ();
955 // so we don't find the current region again..
956 if (dir > 0 || pos > 0) {
960 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
964 _session->request_locate (target);
968 Editor::cursor_to_next_region_boundary (bool with_selection)
970 cursor_to_region_boundary (with_selection, 1);
974 Editor::cursor_to_previous_region_boundary (bool with_selection)
976 cursor_to_region_boundary (with_selection, -1);
980 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
982 boost::shared_ptr<Region> r;
983 samplepos_t pos = cursor->current_sample ();
989 TimeAxisView *ontrack = 0;
991 // so we don't find the current region again..
995 if (!selection->tracks.empty()) {
997 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
999 } else if (clicked_axisview) {
1002 t.push_back (clicked_axisview);
1004 r = find_next_region (pos, point, dir, t, &ontrack);
1008 r = find_next_region (pos, point, dir, track_views, &ontrack);
1017 pos = r->first_sample ();
1021 pos = r->last_sample ();
1025 pos = r->sync_position ();
1029 if (cursor == playhead_cursor) {
1030 _session->request_locate (pos);
1032 cursor->set_position (pos);
1037 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1039 cursor_to_region_point (cursor, point, 1);
1043 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1045 cursor_to_region_point (cursor, point, -1);
1049 Editor::cursor_to_selection_start (EditorCursor *cursor)
1051 samplepos_t pos = 0;
1053 switch (mouse_mode) {
1055 if (!selection->regions.empty()) {
1056 pos = selection->regions.start();
1061 if (!selection->time.empty()) {
1062 pos = selection->time.start ();
1070 if (cursor == playhead_cursor) {
1071 _session->request_locate (pos);
1073 cursor->set_position (pos);
1078 Editor::cursor_to_selection_end (EditorCursor *cursor)
1080 samplepos_t pos = 0;
1082 switch (mouse_mode) {
1084 if (!selection->regions.empty()) {
1085 pos = selection->regions.end_sample();
1090 if (!selection->time.empty()) {
1091 pos = selection->time.end_sample ();
1099 if (cursor == playhead_cursor) {
1100 _session->request_locate (pos);
1102 cursor->set_position (pos);
1107 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1117 if (selection->markers.empty()) {
1121 if (!mouse_sample (mouse, ignored)) {
1125 add_location_mark (mouse);
1128 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1132 samplepos_t pos = loc->start();
1134 // so we don't find the current region again..
1135 if (dir > 0 || pos > 0) {
1139 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1143 loc->move_to (target, 0);
1147 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1149 selected_marker_to_region_boundary (with_selection, 1);
1153 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1155 selected_marker_to_region_boundary (with_selection, -1);
1159 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1161 boost::shared_ptr<Region> r;
1166 if (!_session || selection->markers.empty()) {
1170 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1174 TimeAxisView *ontrack = 0;
1178 // so we don't find the current region again..
1182 if (!selection->tracks.empty()) {
1184 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1188 r = find_next_region (pos, point, dir, track_views, &ontrack);
1197 pos = r->first_sample ();
1201 pos = r->last_sample ();
1205 pos = r->adjust_to_sync (r->first_sample());
1209 loc->move_to (pos, 0);
1213 Editor::selected_marker_to_next_region_point (RegionPoint point)
1215 selected_marker_to_region_point (point, 1);
1219 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1221 selected_marker_to_region_point (point, -1);
1225 Editor::selected_marker_to_selection_start ()
1227 samplepos_t pos = 0;
1231 if (!_session || selection->markers.empty()) {
1235 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1239 switch (mouse_mode) {
1241 if (!selection->regions.empty()) {
1242 pos = selection->regions.start();
1247 if (!selection->time.empty()) {
1248 pos = selection->time.start ();
1256 loc->move_to (pos, 0);
1260 Editor::selected_marker_to_selection_end ()
1262 samplepos_t pos = 0;
1266 if (!_session || selection->markers.empty()) {
1270 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1274 switch (mouse_mode) {
1276 if (!selection->regions.empty()) {
1277 pos = selection->regions.end_sample();
1282 if (!selection->time.empty()) {
1283 pos = selection->time.end_sample ();
1291 loc->move_to (pos, 0);
1295 Editor::scroll_playhead (bool forward)
1297 samplepos_t pos = playhead_cursor->current_sample ();
1298 samplecnt_t delta = (samplecnt_t) floor (current_page_samples() / 0.8);
1301 if (pos == max_samplepos) {
1305 if (pos < max_samplepos - delta) {
1308 pos = max_samplepos;
1324 _session->request_locate (pos);
1328 Editor::cursor_align (bool playhead_to_edit)
1334 if (playhead_to_edit) {
1336 if (selection->markers.empty()) {
1340 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1343 const int32_t divisions = get_grid_music_divisions (0);
1344 /* move selected markers to playhead */
1346 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1349 Location* loc = find_location_from_marker (*i, ignored);
1351 if (loc->is_mark()) {
1352 loc->set_start (playhead_cursor->current_sample (), false, true, divisions);
1354 loc->set (playhead_cursor->current_sample (),
1355 playhead_cursor->current_sample () + loc->length(), true, divisions);
1362 Editor::scroll_backward (float pages)
1364 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1365 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1368 if (_leftmost_sample < cnt) {
1371 sample = _leftmost_sample - cnt;
1374 reset_x_origin (sample);
1378 Editor::scroll_forward (float pages)
1380 samplepos_t const one_page = (samplepos_t) rint (_visible_canvas_width * samples_per_pixel);
1381 samplepos_t const cnt = (samplepos_t) floor (pages * one_page);
1384 if (max_samplepos - cnt < _leftmost_sample) {
1385 sample = max_samplepos - cnt;
1387 sample = _leftmost_sample + cnt;
1390 reset_x_origin (sample);
1394 Editor::scroll_tracks_down ()
1396 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1397 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1398 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1401 vertical_adjustment.set_value (vert_value);
1405 Editor::scroll_tracks_up ()
1407 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1411 Editor::scroll_tracks_down_line ()
1413 double vert_value = vertical_adjustment.get_value() + 60;
1415 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1416 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1419 vertical_adjustment.set_value (vert_value);
1423 Editor::scroll_tracks_up_line ()
1425 reset_y_origin (vertical_adjustment.get_value() - 60);
1429 Editor::select_topmost_track ()
1431 const double top_of_trackviews = vertical_adjustment.get_value();
1432 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1433 if ((*t)->hidden()) {
1436 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1438 selection->set (*t);
1445 Editor::scroll_down_one_track (bool skip_child_views)
1447 TrackViewList::reverse_iterator next = track_views.rend();
1448 const double top_of_trackviews = vertical_adjustment.get_value();
1450 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1451 if ((*t)->hidden()) {
1455 /* If this is the upper-most visible trackview, we want to display
1456 * the one above it (next)
1458 * Note that covers_y_position() is recursive and includes child views
1460 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1463 if (skip_child_views) {
1466 /* automation lane (one level, non-recursive)
1468 * - if no automation lane exists -> move to next tack
1469 * - if the first (here: bottom-most) matches -> move to next tack
1470 * - if no y-axis match is found -> the current track is at the top
1471 * -> move to last (here: top-most) automation lane
1473 TimeAxisView::Children kids = (*t)->get_child_list();
1474 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1476 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1477 if ((*ci)->hidden()) {
1481 std::pair<TimeAxisView*,double> dev;
1482 dev = (*ci)->covers_y_position (top_of_trackviews);
1484 /* some automation lane is currently at the top */
1485 if (ci == kids.rbegin()) {
1486 /* first (bottom-most) autmation lane is at the top.
1487 * -> move to next track
1496 if (nkid != kids.rend()) {
1497 ensure_time_axis_view_is_visible (**nkid, true);
1505 /* move to the track below the first one that covers the */
1507 if (next != track_views.rend()) {
1508 ensure_time_axis_view_is_visible (**next, true);
1516 Editor::scroll_up_one_track (bool skip_child_views)
1518 TrackViewList::iterator prev = track_views.end();
1519 double top_of_trackviews = vertical_adjustment.get_value ();
1521 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1523 if ((*t)->hidden()) {
1527 /* find the trackview at the top of the trackview group
1529 * Note that covers_y_position() is recursive and includes child views
1531 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1534 if (skip_child_views) {
1537 /* automation lane (one level, non-recursive)
1539 * - if no automation lane exists -> move to prev tack
1540 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1541 * (actually last automation lane of previous track, see below)
1542 * - if first (top-most) lane is at the top -> move to this track
1543 * - else move up one lane
1545 TimeAxisView::Children kids = (*t)->get_child_list();
1546 TimeAxisView::Children::iterator pkid = kids.end();
1548 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1549 if ((*ci)->hidden()) {
1553 std::pair<TimeAxisView*,double> dev;
1554 dev = (*ci)->covers_y_position (top_of_trackviews);
1556 /* some automation lane is currently at the top */
1557 if (ci == kids.begin()) {
1558 /* first (top-most) autmation lane is at the top.
1559 * jump directly to this track's top
1561 ensure_time_axis_view_is_visible (**t, true);
1564 else if (pkid != kids.end()) {
1565 /* some other automation lane is at the top.
1566 * move up to prev automation lane.
1568 ensure_time_axis_view_is_visible (**pkid, true);
1571 assert(0); // not reached
1582 if (prev != track_views.end()) {
1583 // move to bottom-most automation-lane of the previous track
1584 TimeAxisView::Children kids = (*prev)->get_child_list();
1585 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1586 if (!skip_child_views) {
1587 // find the last visible lane
1588 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1589 if (!(*ci)->hidden()) {
1595 if (pkid != kids.rend()) {
1596 ensure_time_axis_view_is_visible (**pkid, true);
1598 ensure_time_axis_view_is_visible (**prev, true);
1607 Editor::scroll_left_step ()
1609 samplepos_t xdelta = (current_page_samples() / 8);
1611 if (_leftmost_sample > xdelta) {
1612 reset_x_origin (_leftmost_sample - xdelta);
1620 Editor::scroll_right_step ()
1622 samplepos_t xdelta = (current_page_samples() / 8);
1624 if (max_samplepos - xdelta > _leftmost_sample) {
1625 reset_x_origin (_leftmost_sample + xdelta);
1627 reset_x_origin (max_samplepos - current_page_samples());
1632 Editor::scroll_left_half_page ()
1634 samplepos_t xdelta = (current_page_samples() / 2);
1635 if (_leftmost_sample > xdelta) {
1636 reset_x_origin (_leftmost_sample - xdelta);
1643 Editor::scroll_right_half_page ()
1645 samplepos_t xdelta = (current_page_samples() / 2);
1646 if (max_samplepos - xdelta > _leftmost_sample) {
1647 reset_x_origin (_leftmost_sample + xdelta);
1649 reset_x_origin (max_samplepos - current_page_samples());
1656 Editor::tav_zoom_step (bool coarser)
1658 DisplaySuspender ds;
1662 if (selection->tracks.empty()) {
1665 ts = &selection->tracks;
1668 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1669 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1670 tv->step_height (coarser);
1675 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1677 DisplaySuspender ds;
1681 if (selection->tracks.empty() || force_all) {
1684 ts = &selection->tracks;
1687 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1688 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1689 uint32_t h = tv->current_height ();
1694 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1699 tv->set_height (h + 5);
1705 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1707 Editing::ZoomFocus temp_focus = zoom_focus;
1708 zoom_focus = Editing::ZoomFocusMouse;
1709 temporal_zoom_step_scale (zoom_out, scale);
1710 zoom_focus = temp_focus;
1714 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1716 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1720 Editor::temporal_zoom_step (bool zoom_out)
1722 temporal_zoom_step_scale (zoom_out, 2.0);
1726 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1728 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1730 samplecnt_t nspp = samples_per_pixel;
1734 if (nspp == samples_per_pixel) {
1739 if (nspp == samples_per_pixel) {
1744 //zoom-behavior-tweaks
1745 //limit our maximum zoom to the session gui extents value
1746 std::pair<samplepos_t, samplepos_t> ext = session_gui_extents();
1747 samplecnt_t session_extents_pp = (ext.second - ext.first) / _visible_canvas_width;
1748 if (nspp > session_extents_pp)
1749 nspp = session_extents_pp;
1751 temporal_zoom (nspp);
1755 Editor::temporal_zoom (samplecnt_t fpp)
1761 samplepos_t current_page = current_page_samples();
1762 samplepos_t current_leftmost = _leftmost_sample;
1763 samplepos_t current_rightmost;
1764 samplepos_t current_center;
1765 samplepos_t new_page_size;
1766 samplepos_t half_page_size;
1767 samplepos_t leftmost_after_zoom = 0;
1769 bool in_track_canvas;
1770 bool use_mouse_sample = true;
1774 if (fpp == samples_per_pixel) {
1778 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1779 // segfaults for lack of memory. If somebody decides this is not high enough I
1780 // believe it can be raisen to higher values but some limit must be in place.
1782 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1783 // all of which is used for the editor track displays. The whole day
1784 // would be 4147200000 samples, so 2592000 samples per pixel.
1786 nfpp = min (fpp, (samplecnt_t) 2592000);
1787 nfpp = max ((samplecnt_t) 1, nfpp);
1789 new_page_size = (samplepos_t) floor (_visible_canvas_width * nfpp);
1790 half_page_size = new_page_size / 2;
1792 switch (zoom_focus) {
1794 leftmost_after_zoom = current_leftmost;
1797 case ZoomFocusRight:
1798 current_rightmost = _leftmost_sample + current_page;
1799 if (current_rightmost < new_page_size) {
1800 leftmost_after_zoom = 0;
1802 leftmost_after_zoom = current_rightmost - new_page_size;
1806 case ZoomFocusCenter:
1807 current_center = current_leftmost + (current_page/2);
1808 if (current_center < half_page_size) {
1809 leftmost_after_zoom = 0;
1811 leftmost_after_zoom = current_center - half_page_size;
1815 case ZoomFocusPlayhead:
1816 /* centre playhead */
1817 l = playhead_cursor->current_sample () - (new_page_size * 0.5);
1820 leftmost_after_zoom = 0;
1821 } else if (l > max_samplepos) {
1822 leftmost_after_zoom = max_samplepos - new_page_size;
1824 leftmost_after_zoom = (samplepos_t) l;
1828 case ZoomFocusMouse:
1829 /* try to keep the mouse over the same point in the display */
1831 if (_drags->active()) {
1832 where = _drags->current_pointer_sample ();
1833 } else if (!mouse_sample (where, in_track_canvas)) {
1834 use_mouse_sample = false;
1837 if (use_mouse_sample) {
1838 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1841 leftmost_after_zoom = 0;
1842 } else if (l > max_samplepos) {
1843 leftmost_after_zoom = max_samplepos - new_page_size;
1845 leftmost_after_zoom = (samplepos_t) l;
1848 /* use playhead instead */
1849 where = playhead_cursor->current_sample ();
1851 if (where < half_page_size) {
1852 leftmost_after_zoom = 0;
1854 leftmost_after_zoom = where - half_page_size;
1860 /* try to keep the edit point in the same place */
1861 where = get_preferred_edit_position ();
1865 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1868 leftmost_after_zoom = 0;
1869 } else if (l > max_samplepos) {
1870 leftmost_after_zoom = max_samplepos - new_page_size;
1872 leftmost_after_zoom = (samplepos_t) l;
1876 /* edit point not defined */
1883 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_sample());
1885 reposition_and_zoom (leftmost_after_zoom, nfpp);
1889 Editor::calc_extra_zoom_edges(samplepos_t &start, samplepos_t &end)
1891 /* this func helps make sure we leave a little space
1892 at each end of the editor so that the zoom doesn't fit the region
1893 precisely to the screen.
1896 GdkScreen* screen = gdk_screen_get_default ();
1897 const gint pixwidth = gdk_screen_get_width (screen);
1898 const gint mmwidth = gdk_screen_get_width_mm (screen);
1899 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1900 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1902 const samplepos_t range = end - start;
1903 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1904 const samplepos_t extra_samples = (samplepos_t) floor (one_centimeter_in_pixels * new_fpp);
1906 if (start > extra_samples) {
1907 start -= extra_samples;
1912 if (max_samplepos - extra_samples > end) {
1913 end += extra_samples;
1915 end = max_samplepos;
1920 Editor::get_selection_extents (samplepos_t &start, samplepos_t &end) const
1922 start = max_samplepos;
1926 //ToDo: if notes are selected, set extents to that selection
1928 //ToDo: if control points are selected, set extents to that selection
1930 if (!selection->regions.empty()) {
1931 RegionSelection rs = get_regions_from_selection_and_entered ();
1933 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1935 if ((*i)->region()->position() < start) {
1936 start = (*i)->region()->position();
1939 if ((*i)->region()->last_sample() + 1 > end) {
1940 end = (*i)->region()->last_sample() + 1;
1944 } else if (!selection->time.empty()) {
1945 start = selection->time.start();
1946 end = selection->time.end_sample();
1948 ret = false; //no selection found
1951 if ((start == 0 && end == 0) || end < start) {
1960 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1962 if (!selection) return;
1964 //ToDo: if notes are selected, zoom to that
1966 //ToDo: if control points are selected, zoom to that
1968 if (axes == Horizontal || axes == Both) {
1970 samplepos_t start, end;
1971 if (get_selection_extents (start, end)) {
1972 calc_extra_zoom_edges (start, end);
1973 temporal_zoom_by_sample (start, end);
1977 if (axes == Vertical || axes == Both) {
1981 //normally, we don't do anything "automatic" to the user's selection.
1982 //but in this case, we will clear the selection after a zoom-to-selection.
1987 Editor::temporal_zoom_session ()
1989 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1992 samplecnt_t start = _session->current_start_sample();
1993 samplecnt_t end = _session->current_end_sample();
1995 if (_session->actively_recording ()) {
1996 samplepos_t cur = playhead_cursor->current_sample ();
1998 /* recording beyond the end marker; zoom out
1999 * by 5 seconds more so that if 'follow
2000 * playhead' is active we don't immediately
2003 end = cur + _session->sample_rate() * 5;
2007 if ((start == 0 && end == 0) || end < start) {
2011 calc_extra_zoom_edges(start, end);
2013 temporal_zoom_by_sample (start, end);
2018 Editor::temporal_zoom_extents ()
2020 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2023 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
2025 samplecnt_t start = ext.first;
2026 samplecnt_t end = ext.second;
2028 if (_session->actively_recording ()) {
2029 samplepos_t cur = playhead_cursor->current_sample ();
2031 /* recording beyond the end marker; zoom out
2032 * by 5 seconds more so that if 'follow
2033 * playhead' is active we don't immediately
2036 end = cur + _session->sample_rate() * 5;
2040 if ((start == 0 && end == 0) || end < start) {
2044 calc_extra_zoom_edges(start, end);
2046 temporal_zoom_by_sample (start, end);
2051 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2053 if (!_session) return;
2055 if ((start == 0 && end == 0) || end < start) {
2059 samplepos_t range = end - start;
2061 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2063 samplepos_t new_page = range;
2064 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2065 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2067 if (new_leftmost > middle) {
2071 if (new_leftmost < 0) {
2075 reposition_and_zoom (new_leftmost, new_fpp);
2079 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2085 samplecnt_t range_before = sample - _leftmost_sample;
2086 samplecnt_t new_spp;
2089 if (samples_per_pixel <= 1) {
2092 new_spp = samples_per_pixel + (samples_per_pixel/2);
2094 range_before += range_before/2;
2096 if (samples_per_pixel >= 1) {
2097 new_spp = samples_per_pixel - (samples_per_pixel/2);
2099 /* could bail out here since we cannot zoom any finer,
2100 but leave that to the equality test below
2102 new_spp = samples_per_pixel;
2105 range_before -= range_before/2;
2108 if (new_spp == samples_per_pixel) {
2112 /* zoom focus is automatically taken as @param sample when this
2116 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2118 if (new_leftmost > sample) {
2122 if (new_leftmost < 0) {
2126 reposition_and_zoom (new_leftmost, new_spp);
2131 Editor::choose_new_marker_name(string &name) {
2133 if (!UIConfiguration::instance().get_name_new_markers()) {
2134 /* don't prompt user for a new name */
2138 Prompter dialog (true);
2140 dialog.set_prompt (_("New Name:"));
2142 dialog.set_title (_("New Location Marker"));
2144 dialog.set_name ("MarkNameWindow");
2145 dialog.set_size_request (250, -1);
2146 dialog.set_position (Gtk::WIN_POS_MOUSE);
2148 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2149 dialog.set_initial_text (name);
2153 switch (dialog.run ()) {
2154 case RESPONSE_ACCEPT:
2160 dialog.get_result(name);
2167 Editor::add_location_from_selection ()
2171 if (selection->time.empty()) {
2175 if (_session == 0 || clicked_axisview == 0) {
2179 samplepos_t start = selection->time[clicked_selection].start;
2180 samplepos_t end = selection->time[clicked_selection].end;
2182 _session->locations()->next_available_name(rangename,"selection");
2183 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2185 begin_reversible_command (_("add marker"));
2187 XMLNode &before = _session->locations()->get_state();
2188 _session->locations()->add (location, true);
2189 XMLNode &after = _session->locations()->get_state();
2190 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2192 commit_reversible_command ();
2196 Editor::add_location_mark (samplepos_t where)
2200 select_new_marker = true;
2202 _session->locations()->next_available_name(markername,"mark");
2203 if (!choose_new_marker_name(markername)) {
2206 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2207 begin_reversible_command (_("add marker"));
2209 XMLNode &before = _session->locations()->get_state();
2210 _session->locations()->add (location, true);
2211 XMLNode &after = _session->locations()->get_state();
2212 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2214 commit_reversible_command ();
2218 Editor::set_session_start_from_playhead ()
2224 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2225 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2227 XMLNode &before = loc->get_state();
2229 _session->set_session_extents (_session->audible_sample(), loc->end());
2231 XMLNode &after = loc->get_state();
2233 begin_reversible_command (_("Set session start"));
2235 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2237 commit_reversible_command ();
2242 Editor::set_session_end_from_playhead ()
2248 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2249 _session->set_session_extents (_session->audible_sample(), _session->audible_sample());
2251 XMLNode &before = loc->get_state();
2253 _session->set_session_extents (loc->start(), _session->audible_sample());
2255 XMLNode &after = loc->get_state();
2257 begin_reversible_command (_("Set session start"));
2259 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2261 commit_reversible_command ();
2264 _session->set_end_is_free (false);
2269 Editor::toggle_location_at_playhead_cursor ()
2271 if (!do_remove_location_at_playhead_cursor())
2273 add_location_from_playhead_cursor();
2278 Editor::add_location_from_playhead_cursor ()
2280 add_location_mark (_session->audible_sample());
2284 Editor::do_remove_location_at_playhead_cursor ()
2286 bool removed = false;
2289 XMLNode &before = _session->locations()->get_state();
2291 //find location(s) at this time
2292 Locations::LocationList locs;
2293 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2294 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2295 if ((*i)->is_mark()) {
2296 _session->locations()->remove (*i);
2303 begin_reversible_command (_("remove marker"));
2304 XMLNode &after = _session->locations()->get_state();
2305 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2306 commit_reversible_command ();
2313 Editor::remove_location_at_playhead_cursor ()
2315 do_remove_location_at_playhead_cursor ();
2318 /** Add a range marker around each selected region */
2320 Editor::add_locations_from_region ()
2322 RegionSelection rs = get_regions_from_selection_and_entered ();
2327 bool commit = false;
2329 XMLNode &before = _session->locations()->get_state();
2331 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2333 boost::shared_ptr<Region> region = (*i)->region ();
2335 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2337 _session->locations()->add (location, true);
2342 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2343 XMLNode &after = _session->locations()->get_state();
2344 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2345 commit_reversible_command ();
2349 /** Add a single range marker around all selected regions */
2351 Editor::add_location_from_region ()
2353 RegionSelection rs = get_regions_from_selection_and_entered ();
2359 XMLNode &before = _session->locations()->get_state();
2363 if (rs.size() > 1) {
2364 _session->locations()->next_available_name(markername, "regions");
2366 RegionView* rv = *(rs.begin());
2367 boost::shared_ptr<Region> region = rv->region();
2368 markername = region->name();
2371 if (!choose_new_marker_name(markername)) {
2375 // single range spanning all selected
2376 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2377 _session->locations()->add (location, true);
2379 begin_reversible_command (_("add marker"));
2380 XMLNode &after = _session->locations()->get_state();
2381 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2382 commit_reversible_command ();
2388 Editor::jump_forward_to_mark ()
2394 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2400 _session->request_locate (pos, _session->transport_rolling());
2404 Editor::jump_backward_to_mark ()
2410 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2412 //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...
2413 if (_session->transport_rolling()) {
2414 if ((playhead_cursor->current_sample() - pos) < _session->sample_rate()/2) {
2415 samplepos_t prior = _session->locations()->first_mark_before (pos);
2424 _session->request_locate (pos, _session->transport_rolling());
2430 samplepos_t const pos = _session->audible_sample ();
2433 _session->locations()->next_available_name (markername, "mark");
2435 if (!choose_new_marker_name (markername)) {
2439 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2443 Editor::clear_markers ()
2446 begin_reversible_command (_("clear markers"));
2448 XMLNode &before = _session->locations()->get_state();
2449 _session->locations()->clear_markers ();
2450 XMLNode &after = _session->locations()->get_state();
2451 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2453 commit_reversible_command ();
2458 Editor::clear_ranges ()
2461 begin_reversible_command (_("clear ranges"));
2463 XMLNode &before = _session->locations()->get_state();
2465 _session->locations()->clear_ranges ();
2467 XMLNode &after = _session->locations()->get_state();
2468 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2470 commit_reversible_command ();
2475 Editor::clear_locations ()
2477 begin_reversible_command (_("clear locations"));
2479 XMLNode &before = _session->locations()->get_state();
2480 _session->locations()->clear ();
2481 XMLNode &after = _session->locations()->get_state();
2482 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2484 commit_reversible_command ();
2488 Editor::unhide_markers ()
2490 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2491 Location *l = (*i).first;
2492 if (l->is_hidden() && l->is_mark()) {
2493 l->set_hidden(false, this);
2499 Editor::unhide_ranges ()
2501 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2502 Location *l = (*i).first;
2503 if (l->is_hidden() && l->is_range_marker()) {
2504 l->set_hidden(false, this);
2509 /* INSERT/REPLACE */
2512 Editor::insert_region_list_selection (float times)
2514 RouteTimeAxisView *tv = 0;
2515 boost::shared_ptr<Playlist> playlist;
2517 if (clicked_routeview != 0) {
2518 tv = clicked_routeview;
2519 } else if (!selection->tracks.empty()) {
2520 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2523 } else if (entered_track != 0) {
2524 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2531 if ((playlist = tv->playlist()) == 0) {
2535 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2540 begin_reversible_command (_("insert region"));
2541 playlist->clear_changes ();
2542 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2543 if (Config->get_edit_mode() == Ripple)
2544 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2546 _session->add_command(new StatefulDiffCommand (playlist));
2547 commit_reversible_command ();
2550 /* BUILT-IN EFFECTS */
2553 Editor::reverse_selection ()
2558 /* GAIN ENVELOPE EDITING */
2561 Editor::edit_envelope ()
2568 Editor::transition_to_rolling (bool fwd)
2574 if (_session->config.get_external_sync()) {
2575 switch (Config->get_sync_source()) {
2579 /* transport controlled by the master */
2584 if (_session->is_auditioning()) {
2585 _session->cancel_audition ();
2589 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2593 Editor::play_from_start ()
2595 _session->request_locate (_session->current_start_sample(), true);
2599 Editor::play_from_edit_point ()
2601 _session->request_locate (get_preferred_edit_position(), true);
2605 Editor::play_from_edit_point_and_return ()
2607 samplepos_t start_sample;
2608 samplepos_t return_sample;
2610 start_sample = get_preferred_edit_position (EDIT_IGNORE_PHEAD);
2612 if (_session->transport_rolling()) {
2613 _session->request_locate (start_sample, false);
2617 /* don't reset the return sample if its already set */
2619 if ((return_sample = _session->requested_return_sample()) < 0) {
2620 return_sample = _session->audible_sample();
2623 if (start_sample >= 0) {
2624 _session->request_roll_at_and_return (start_sample, return_sample);
2629 Editor::play_selection ()
2631 samplepos_t start, end;
2632 if (!get_selection_extents (start, end))
2635 AudioRange ar (start, end, 0);
2636 list<AudioRange> lar;
2639 _session->request_play_range (&lar, true);
2644 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2646 if (_session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync())
2649 location -= _session->preroll_samples (location);
2651 //don't try to locate before the beginning of time
2656 //if follow_playhead is on, keep the playhead on the screen
2657 if (_follow_playhead)
2658 if (location < _leftmost_sample)
2659 location = _leftmost_sample;
2661 _session->request_locate (location);
2665 Editor::play_with_preroll ()
2667 samplepos_t start, end;
2668 if (UIConfiguration::instance().get_follow_edits() && get_selection_extents (start, end)) {
2669 const samplepos_t preroll = _session->preroll_samples (start);
2671 samplepos_t ret = start;
2673 if (start > preroll) {
2674 start = start - preroll;
2677 end = end + preroll; //"post-roll"
2679 AudioRange ar (start, end, 0);
2680 list<AudioRange> lar;
2683 _session->request_play_range (&lar, true);
2684 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2686 samplepos_t ph = playhead_cursor->current_sample ();
2687 const samplepos_t preroll = _session->preroll_samples (ph);
2690 start = ph - preroll;
2694 _session->request_locate (start, true);
2695 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2700 Editor::rec_with_preroll ()
2702 samplepos_t ph = playhead_cursor->current_sample ();
2703 samplepos_t preroll = _session->preroll_samples (ph);
2704 _session->request_preroll_record_trim (ph, preroll);
2708 Editor::rec_with_count_in ()
2710 _session->request_count_in_record ();
2714 Editor::play_location (Location& location)
2716 if (location.start() <= location.end()) {
2720 _session->request_bounded_roll (location.start(), location.end());
2724 Editor::loop_location (Location& location)
2726 if (location.start() <= location.end()) {
2732 if ((tll = transport_loop_location()) != 0) {
2733 tll->set (location.start(), location.end());
2735 // enable looping, reposition and start rolling
2736 _session->request_locate (tll->start(), true);
2737 _session->request_play_loop (true);
2742 Editor::do_layer_operation (LayerOperation op)
2744 if (selection->regions.empty ()) {
2748 bool const multiple = selection->regions.size() > 1;
2752 begin_reversible_command (_("raise regions"));
2754 begin_reversible_command (_("raise region"));
2760 begin_reversible_command (_("raise regions to top"));
2762 begin_reversible_command (_("raise region to top"));
2768 begin_reversible_command (_("lower regions"));
2770 begin_reversible_command (_("lower region"));
2776 begin_reversible_command (_("lower regions to bottom"));
2778 begin_reversible_command (_("lower region"));
2783 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2784 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2785 (*i)->clear_owned_changes ();
2788 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2789 boost::shared_ptr<Region> r = (*i)->region ();
2801 r->lower_to_bottom ();
2805 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2806 vector<Command*> cmds;
2808 _session->add_commands (cmds);
2811 commit_reversible_command ();
2815 Editor::raise_region ()
2817 do_layer_operation (Raise);
2821 Editor::raise_region_to_top ()
2823 do_layer_operation (RaiseToTop);
2827 Editor::lower_region ()
2829 do_layer_operation (Lower);
2833 Editor::lower_region_to_bottom ()
2835 do_layer_operation (LowerToBottom);
2838 /** Show the region editor for the selected regions */
2840 Editor::show_region_properties ()
2842 selection->foreach_regionview (&RegionView::show_region_editor);
2845 /** Show the midi list editor for the selected MIDI regions */
2847 Editor::show_midi_list_editor ()
2849 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2853 Editor::rename_region ()
2855 RegionSelection rs = get_regions_from_selection_and_entered ();
2861 ArdourDialog d (_("Rename Region"), true, false);
2863 Label label (_("New name:"));
2866 hbox.set_spacing (6);
2867 hbox.pack_start (label, false, false);
2868 hbox.pack_start (entry, true, true);
2870 d.get_vbox()->set_border_width (12);
2871 d.get_vbox()->pack_start (hbox, false, false);
2873 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2874 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2876 d.set_size_request (300, -1);
2878 entry.set_text (rs.front()->region()->name());
2879 entry.select_region (0, -1);
2881 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2887 int const ret = d.run();
2891 if (ret != RESPONSE_OK) {
2895 std::string str = entry.get_text();
2896 strip_whitespace_edges (str);
2898 rs.front()->region()->set_name (str);
2899 _regions->redisplay ();
2903 /** Start an audition of the first selected region */
2905 Editor::play_edit_range ()
2907 samplepos_t start, end;
2909 if (get_edit_op_range (start, end)) {
2910 _session->request_bounded_roll (start, end);
2915 Editor::play_selected_region ()
2917 samplepos_t start = max_samplepos;
2918 samplepos_t end = 0;
2920 RegionSelection rs = get_regions_from_selection_and_entered ();
2926 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2927 if ((*i)->region()->position() < start) {
2928 start = (*i)->region()->position();
2930 if ((*i)->region()->last_sample() + 1 > end) {
2931 end = (*i)->region()->last_sample() + 1;
2935 _session->request_bounded_roll (start, end);
2939 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2941 _session->audition_region (region);
2945 Editor::region_from_selection ()
2947 if (clicked_axisview == 0) {
2951 if (selection->time.empty()) {
2955 samplepos_t start = selection->time[clicked_selection].start;
2956 samplepos_t end = selection->time[clicked_selection].end;
2958 TrackViewList tracks = get_tracks_for_range_action ();
2960 samplepos_t selection_cnt = end - start + 1;
2962 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2963 boost::shared_ptr<Region> current;
2964 boost::shared_ptr<Playlist> pl;
2965 samplepos_t internal_start;
2968 if ((pl = (*i)->playlist()) == 0) {
2972 if ((current = pl->top_region_at (start)) == 0) {
2976 internal_start = start - current->position();
2977 RegionFactory::region_name (new_name, current->name(), true);
2981 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2982 plist.add (ARDOUR::Properties::length, selection_cnt);
2983 plist.add (ARDOUR::Properties::name, new_name);
2984 plist.add (ARDOUR::Properties::layer, 0);
2986 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2991 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2993 if (selection->time.empty() || selection->tracks.empty()) {
2997 samplepos_t start, end;
2998 if (clicked_selection) {
2999 start = selection->time[clicked_selection].start;
3000 end = selection->time[clicked_selection].end;
3002 start = selection->time.start();
3003 end = selection->time.end_sample();
3006 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3007 sort_track_selection (ts);
3009 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3010 boost::shared_ptr<Region> current;
3011 boost::shared_ptr<Playlist> playlist;
3012 samplepos_t internal_start;
3015 if ((playlist = (*i)->playlist()) == 0) {
3019 if ((current = playlist->top_region_at(start)) == 0) {
3023 internal_start = start - current->position();
3024 RegionFactory::region_name (new_name, current->name(), true);
3028 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3029 plist.add (ARDOUR::Properties::length, end - start + 1);
3030 plist.add (ARDOUR::Properties::name, new_name);
3032 new_regions.push_back (RegionFactory::create (current, plist));
3037 Editor::split_multichannel_region ()
3039 RegionSelection rs = get_regions_from_selection_and_entered ();
3045 vector< boost::shared_ptr<Region> > v;
3047 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3048 (*x)->region()->separate_by_channel (v);
3053 Editor::new_region_from_selection ()
3055 region_from_selection ();
3056 cancel_selection ();
3060 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3062 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3063 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3064 case Evoral::OverlapNone:
3072 * - selected tracks, or if there are none...
3073 * - tracks containing selected regions, or if there are none...
3078 Editor::get_tracks_for_range_action () const
3082 if (selection->tracks.empty()) {
3084 /* use tracks with selected regions */
3086 RegionSelection rs = selection->regions;
3088 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3089 TimeAxisView* tv = &(*i)->get_time_axis_view();
3091 if (!t.contains (tv)) {
3097 /* no regions and no tracks: use all tracks */
3103 t = selection->tracks;
3106 return t.filter_to_unique_playlists();
3110 Editor::separate_regions_between (const TimeSelection& ts)
3112 bool in_command = false;
3113 boost::shared_ptr<Playlist> playlist;
3114 RegionSelection new_selection;
3116 TrackViewList tmptracks = get_tracks_for_range_action ();
3117 sort_track_selection (tmptracks);
3119 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3121 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3127 if (!rtv->is_track()) {
3131 /* no edits to destructive tracks */
3133 if (rtv->track()->destructive()) {
3137 if ((playlist = rtv->playlist()) != 0) {
3139 playlist->clear_changes ();
3141 /* XXX need to consider musical time selections here at some point */
3143 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3145 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3146 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3148 latest_regionviews.clear ();
3150 playlist->partition ((*t).start, (*t).end, false);
3154 if (!latest_regionviews.empty()) {
3156 rtv->view()->foreach_regionview (sigc::bind (
3157 sigc::ptr_fun (add_if_covered),
3158 &(*t), &new_selection));
3161 begin_reversible_command (_("separate"));
3165 /* pick up changes to existing regions */
3167 vector<Command*> cmds;
3168 playlist->rdiff (cmds);
3169 _session->add_commands (cmds);
3171 /* pick up changes to the playlist itself (adds/removes)
3174 _session->add_command(new StatefulDiffCommand (playlist));
3181 // selection->set (new_selection);
3183 commit_reversible_command ();
3187 struct PlaylistState {
3188 boost::shared_ptr<Playlist> playlist;
3192 /** Take tracks from get_tracks_for_range_action and cut any regions
3193 * on those tracks so that the tracks are empty over the time
3197 Editor::separate_region_from_selection ()
3199 /* preferentially use *all* ranges in the time selection if we're in range mode
3200 to allow discontiguous operation, since get_edit_op_range() currently
3201 returns a single range.
3204 if (!selection->time.empty()) {
3206 separate_regions_between (selection->time);
3213 if (get_edit_op_range (start, end)) {
3215 AudioRange ar (start, end, 1);
3219 separate_regions_between (ts);
3225 Editor::separate_region_from_punch ()
3227 Location* loc = _session->locations()->auto_punch_location();
3229 separate_regions_using_location (*loc);
3234 Editor::separate_region_from_loop ()
3236 Location* loc = _session->locations()->auto_loop_location();
3238 separate_regions_using_location (*loc);
3243 Editor::separate_regions_using_location (Location& loc)
3245 if (loc.is_mark()) {
3249 AudioRange ar (loc.start(), loc.end(), 1);
3254 separate_regions_between (ts);
3257 /** Separate regions under the selected region */
3259 Editor::separate_under_selected_regions ()
3261 vector<PlaylistState> playlists;
3265 rs = get_regions_from_selection_and_entered();
3267 if (!_session || rs.empty()) {
3271 begin_reversible_command (_("separate region under"));
3273 list<boost::shared_ptr<Region> > regions_to_remove;
3275 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3276 // we can't just remove the region(s) in this loop because
3277 // this removes them from the RegionSelection, and they thus
3278 // disappear from underneath the iterator, and the ++i above
3279 // SEGVs in a puzzling fashion.
3281 // so, first iterate over the regions to be removed from rs and
3282 // add them to the regions_to_remove list, and then
3283 // iterate over the list to actually remove them.
3285 regions_to_remove.push_back ((*i)->region());
3288 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3290 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3293 // is this check necessary?
3297 vector<PlaylistState>::iterator i;
3299 //only take state if this is a new playlist.
3300 for (i = playlists.begin(); i != playlists.end(); ++i) {
3301 if ((*i).playlist == playlist) {
3306 if (i == playlists.end()) {
3308 PlaylistState before;
3309 before.playlist = playlist;
3310 before.before = &playlist->get_state();
3311 playlist->clear_changes ();
3312 playlist->freeze ();
3313 playlists.push_back(before);
3316 //Partition on the region bounds
3317 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3319 //Re-add region that was just removed due to the partition operation
3320 playlist->add_region ((*rl), (*rl)->first_sample());
3323 vector<PlaylistState>::iterator pl;
3325 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3326 (*pl).playlist->thaw ();
3327 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3330 commit_reversible_command ();
3334 Editor::crop_region_to_selection ()
3336 if (!selection->time.empty()) {
3338 begin_reversible_command (_("Crop Regions to Time Selection"));
3339 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3340 crop_region_to ((*i).start, (*i).end);
3342 commit_reversible_command();
3348 if (get_edit_op_range (start, end)) {
3349 begin_reversible_command (_("Crop Regions to Edit Range"));
3351 crop_region_to (start, end);
3353 commit_reversible_command();
3360 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3362 vector<boost::shared_ptr<Playlist> > playlists;
3363 boost::shared_ptr<Playlist> playlist;
3366 if (selection->tracks.empty()) {
3367 ts = track_views.filter_to_unique_playlists();
3369 ts = selection->tracks.filter_to_unique_playlists ();
3372 sort_track_selection (ts);
3374 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3376 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3382 boost::shared_ptr<Track> t = rtv->track();
3384 if (t != 0 && ! t->destructive()) {
3386 if ((playlist = rtv->playlist()) != 0) {
3387 playlists.push_back (playlist);
3392 if (playlists.empty()) {
3397 samplepos_t new_start;
3398 samplepos_t new_end;
3399 samplecnt_t new_length;
3401 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3403 /* Only the top regions at start and end have to be cropped */
3404 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3405 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3407 vector<boost::shared_ptr<Region> > regions;
3409 if (region_at_start != 0) {
3410 regions.push_back (region_at_start);
3412 if (region_at_end != 0) {
3413 regions.push_back (region_at_end);
3416 /* now adjust lengths */
3417 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3419 pos = (*i)->position();
3420 new_start = max (start, pos);
3421 if (max_samplepos - pos > (*i)->length()) {
3422 new_end = pos + (*i)->length() - 1;
3424 new_end = max_samplepos;
3426 new_end = min (end, new_end);
3427 new_length = new_end - new_start + 1;
3429 (*i)->clear_changes ();
3430 (*i)->trim_to (new_start, new_length);
3431 _session->add_command (new StatefulDiffCommand (*i));
3437 Editor::region_fill_track ()
3439 boost::shared_ptr<Playlist> playlist;
3440 RegionSelection regions = get_regions_from_selection_and_entered ();
3441 RegionSelection foo;
3443 samplepos_t const end = _session->current_end_sample ();
3445 if (regions.empty () || regions.end_sample () + 1 >= end) {
3449 samplepos_t const start_sample = regions.start ();
3450 samplepos_t const end_sample = regions.end_sample ();
3451 samplecnt_t const gap = end_sample - start_sample + 1;
3453 begin_reversible_command (Operations::region_fill);
3455 selection->clear_regions ();
3457 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3459 boost::shared_ptr<Region> r ((*i)->region());
3461 TimeAxisView& tv = (*i)->get_time_axis_view();
3462 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3463 latest_regionviews.clear ();
3464 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3466 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3467 playlist = (*i)->region()->playlist();
3468 playlist->clear_changes ();
3469 playlist->duplicate_until (r, position, gap, end);
3470 _session->add_command(new StatefulDiffCommand (playlist));
3474 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3478 selection->set (foo);
3481 commit_reversible_command ();
3485 Editor::set_region_sync_position ()
3487 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3491 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3493 bool in_command = false;
3495 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3497 if (!(*r)->region()->covers (where)) {
3501 boost::shared_ptr<Region> region ((*r)->region());
3504 begin_reversible_command (_("set sync point"));
3508 region->clear_changes ();
3509 region->set_sync_position (where);
3510 _session->add_command(new StatefulDiffCommand (region));
3514 commit_reversible_command ();
3518 /** Remove the sync positions of the selection */
3520 Editor::remove_region_sync ()
3522 RegionSelection rs = get_regions_from_selection_and_entered ();
3528 begin_reversible_command (_("remove region sync"));
3530 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3532 (*i)->region()->clear_changes ();
3533 (*i)->region()->clear_sync_position ();
3534 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3537 commit_reversible_command ();
3541 Editor::naturalize_region ()
3543 RegionSelection rs = get_regions_from_selection_and_entered ();
3549 if (rs.size() > 1) {
3550 begin_reversible_command (_("move regions to original position"));
3552 begin_reversible_command (_("move region to original position"));
3555 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3556 (*i)->region()->clear_changes ();
3557 (*i)->region()->move_to_natural_position ();
3558 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3561 commit_reversible_command ();
3565 Editor::align_regions (RegionPoint what)
3567 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3573 begin_reversible_command (_("align selection"));
3575 samplepos_t const position = get_preferred_edit_position ();
3577 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3578 align_region_internal ((*i)->region(), what, position);
3581 commit_reversible_command ();
3584 struct RegionSortByTime {
3585 bool operator() (const RegionView* a, const RegionView* b) {
3586 return a->region()->position() < b->region()->position();
3591 Editor::align_regions_relative (RegionPoint point)
3593 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3599 samplepos_t const position = get_preferred_edit_position ();
3601 samplepos_t distance = 0;
3602 samplepos_t pos = 0;
3605 list<RegionView*> sorted;
3606 rs.by_position (sorted);
3608 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3613 if (position > r->position()) {
3614 distance = position - r->position();
3616 distance = r->position() - position;
3622 if (position > r->last_sample()) {
3623 distance = position - r->last_sample();
3624 pos = r->position() + distance;
3626 distance = r->last_sample() - position;
3627 pos = r->position() - distance;
3633 pos = r->adjust_to_sync (position);
3634 if (pos > r->position()) {
3635 distance = pos - r->position();
3637 distance = r->position() - pos;
3643 if (pos == r->position()) {
3647 begin_reversible_command (_("align selection (relative)"));
3649 /* move first one specially */
3651 r->clear_changes ();
3652 r->set_position (pos);
3653 _session->add_command(new StatefulDiffCommand (r));
3655 /* move rest by the same amount */
3659 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3661 boost::shared_ptr<Region> region ((*i)->region());
3663 region->clear_changes ();
3666 region->set_position (region->position() + distance);
3668 region->set_position (region->position() - distance);
3671 _session->add_command(new StatefulDiffCommand (region));
3675 commit_reversible_command ();
3679 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3681 begin_reversible_command (_("align region"));
3682 align_region_internal (region, point, position);
3683 commit_reversible_command ();
3687 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3689 region->clear_changes ();
3693 region->set_position (region->adjust_to_sync (position));
3697 if (position > region->length()) {
3698 region->set_position (position - region->length());
3703 region->set_position (position);
3707 _session->add_command(new StatefulDiffCommand (region));
3711 Editor::trim_region_front ()
3717 Editor::trim_region_back ()
3719 trim_region (false);
3723 Editor::trim_region (bool front)
3725 samplepos_t where = get_preferred_edit_position();
3726 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3732 begin_reversible_command (front ? _("trim front") : _("trim back"));
3734 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3735 if (!(*i)->region()->locked()) {
3737 (*i)->region()->clear_changes ();
3740 (*i)->region()->trim_front (where);
3742 (*i)->region()->trim_end (where);
3745 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3749 commit_reversible_command ();
3752 /** Trim the end of the selected regions to the position of the edit cursor */
3754 Editor::trim_region_to_loop ()
3756 Location* loc = _session->locations()->auto_loop_location();
3760 trim_region_to_location (*loc, _("trim to loop"));
3764 Editor::trim_region_to_punch ()
3766 Location* loc = _session->locations()->auto_punch_location();
3770 trim_region_to_location (*loc, _("trim to punch"));
3774 Editor::trim_region_to_location (const Location& loc, const char* str)
3776 RegionSelection rs = get_regions_from_selection_and_entered ();
3777 bool in_command = false;
3779 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3780 RegionView* rv = (*x);
3782 /* require region to span proposed trim */
3783 switch (rv->region()->coverage (loc.start(), loc.end())) {
3784 case Evoral::OverlapInternal:
3790 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3798 start = loc.start();
3801 rv->region()->clear_changes ();
3802 rv->region()->trim_to (start, (end - start));
3805 begin_reversible_command (str);
3808 _session->add_command(new StatefulDiffCommand (rv->region()));
3812 commit_reversible_command ();
3817 Editor::trim_region_to_previous_region_end ()
3819 return trim_to_region(false);
3823 Editor::trim_region_to_next_region_start ()
3825 return trim_to_region(true);
3829 Editor::trim_to_region(bool forward)
3831 RegionSelection rs = get_regions_from_selection_and_entered ();
3832 bool in_command = false;
3834 boost::shared_ptr<Region> next_region;
3836 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3838 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3844 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3850 boost::shared_ptr<Region> region = arv->region();
3851 boost::shared_ptr<Playlist> playlist (region->playlist());
3853 region->clear_changes ();
3857 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3863 region->trim_end (next_region->first_sample() - 1);
3864 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3868 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3874 region->trim_front (next_region->last_sample() + 1);
3875 arv->region_changed (ARDOUR::bounds_change);
3879 begin_reversible_command (_("trim to region"));
3882 _session->add_command(new StatefulDiffCommand (region));
3886 commit_reversible_command ();
3891 Editor::unfreeze_route ()
3893 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3897 clicked_routeview->track()->unfreeze ();
3901 Editor::_freeze_thread (void* arg)
3903 return static_cast<Editor*>(arg)->freeze_thread ();
3907 Editor::freeze_thread ()
3909 /* create event pool because we may need to talk to the session */
3910 SessionEvent::create_per_thread_pool ("freeze events", 64);
3911 /* create per-thread buffers for process() tree to use */
3912 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3913 current_interthread_info->done = true;
3918 Editor::freeze_route ()
3924 /* stop transport before we start. this is important */
3926 _session->request_transport_speed (0.0);
3928 /* wait for just a little while, because the above call is asynchronous */
3930 Glib::usleep (250000);
3932 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3936 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3938 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3939 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3941 d.set_title (_("Cannot freeze"));
3946 if (clicked_routeview->track()->has_external_redirects()) {
3947 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"
3948 "Freezing will only process the signal as far as the first send/insert/return."),
3949 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3951 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3952 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3953 d.set_title (_("Freeze Limits"));
3955 int response = d.run ();
3958 case Gtk::RESPONSE_CANCEL:
3965 InterThreadInfo itt;
3966 current_interthread_info = &itt;
3968 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3970 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3972 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3974 while (!itt.done && !itt.cancel) {
3975 gtk_main_iteration ();
3978 pthread_join (itt.thread, 0);
3979 current_interthread_info = 0;
3983 Editor::bounce_range_selection (bool replace, bool enable_processing)
3985 if (selection->time.empty()) {
3989 TrackSelection views = selection->tracks;
3991 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3993 if (enable_processing) {
3995 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3997 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3999 _("You can't perform this operation because the processing of the signal "
4000 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4001 "You can do this without processing, which is a different operation.")
4003 d.set_title (_("Cannot bounce"));
4010 samplepos_t start = selection->time[clicked_selection].start;
4011 samplepos_t end = selection->time[clicked_selection].end;
4012 samplepos_t cnt = end - start + 1;
4013 bool in_command = false;
4015 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4017 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4023 boost::shared_ptr<Playlist> playlist;
4025 if ((playlist = rtv->playlist()) == 0) {
4029 InterThreadInfo itt;
4031 playlist->clear_changes ();
4032 playlist->clear_owned_changes ();
4034 boost::shared_ptr<Region> r;
4036 if (enable_processing) {
4037 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4039 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4047 list<AudioRange> ranges;
4048 ranges.push_back (AudioRange (start, start+cnt, 0));
4049 playlist->cut (ranges); // discard result
4050 playlist->add_region (r, start);
4054 begin_reversible_command (_("bounce range"));
4057 vector<Command*> cmds;
4058 playlist->rdiff (cmds);
4059 _session->add_commands (cmds);
4061 _session->add_command (new StatefulDiffCommand (playlist));
4065 commit_reversible_command ();
4069 /** Delete selected regions, automation points or a time range */
4073 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4074 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4075 bool deleted = false;
4076 if (current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip())
4077 deleted = current_mixer_strip->delete_processors ();
4083 /** Cut selected regions, automation points or a time range */
4090 /** Copy selected regions, automation points or a time range */
4098 /** @return true if a Cut, Copy or Clear is possible */
4100 Editor::can_cut_copy () const
4102 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4109 /** Cut, copy or clear selected regions, automation points or a time range.
4110 * @param op Operation (Delete, Cut, Copy or Clear)
4113 Editor::cut_copy (CutCopyOp op)
4115 /* only cancel selection if cut/copy is successful.*/
4121 opname = _("delete");
4130 opname = _("clear");
4134 /* if we're deleting something, and the mouse is still pressed,
4135 the thing we started a drag for will be gone when we release
4136 the mouse button(s). avoid this. see part 2 at the end of
4140 if (op == Delete || op == Cut || op == Clear) {
4141 if (_drags->active ()) {
4146 if (op != Delete) { //"Delete" doesn't change copy/paste buf
4147 cut_buffer->clear ();
4150 if (entered_marker) {
4152 /* cut/delete op while pointing at a marker */
4155 Location* loc = find_location_from_marker (entered_marker, ignored);
4157 if (_session && loc) {
4158 entered_marker = NULL;
4159 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4166 switch (mouse_mode) {
4169 begin_reversible_command (opname + ' ' + X_("MIDI"));
4171 commit_reversible_command ();
4177 bool did_edit = false;
4179 if (!selection->regions.empty() || !selection->points.empty()) {
4180 begin_reversible_command (opname + ' ' + _("objects"));
4183 if (!selection->regions.empty()) {
4184 cut_copy_regions (op, selection->regions);
4186 if (op == Cut || op == Delete) {
4187 selection->clear_regions ();
4191 if (!selection->points.empty()) {
4192 cut_copy_points (op);
4194 if (op == Cut || op == Delete) {
4195 selection->clear_points ();
4198 } else if (selection->time.empty()) {
4199 samplepos_t start, end;
4200 /* no time selection, see if we can get an edit range
4203 if (get_edit_op_range (start, end)) {
4204 selection->set (start, end);
4206 } else if (!selection->time.empty()) {
4207 begin_reversible_command (opname + ' ' + _("range"));
4210 cut_copy_ranges (op);
4212 if (op == Cut || op == Delete) {
4213 selection->clear_time ();
4218 /* reset repeated paste state */
4220 last_paste_pos = -1;
4221 commit_reversible_command ();
4224 if (op == Delete || op == Cut || op == Clear) {
4230 struct AutomationRecord {
4231 AutomationRecord () : state (0) , line(NULL) {}
4232 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4234 XMLNode* state; ///< state before any operation
4235 const AutomationLine* line; ///< line this came from
4236 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4239 struct PointsSelectionPositionSorter {
4240 bool operator() (ControlPoint* a, ControlPoint* b) {
4241 return (*(a->model()))->when < (*(b->model()))->when;
4245 /** Cut, copy or clear selected automation points.
4246 * @param op Operation (Cut, Copy or Clear)
4249 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4251 if (selection->points.empty ()) {
4255 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4256 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4258 /* Keep a record of the AutomationLists that we end up using in this operation */
4259 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4262 /* user could select points in any order */
4263 selection->points.sort(PointsSelectionPositionSorter ());
4265 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4266 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4267 const AutomationLine& line = (*sel_point)->line();
4268 const boost::shared_ptr<AutomationList> al = line.the_list();
4269 if (lists.find (al) == lists.end ()) {
4270 /* We haven't seen this list yet, so make a record for it. This includes
4271 taking a copy of its current state, in case this is needed for undo later.
4273 lists[al] = AutomationRecord (&al->get_state (), &line);
4277 if (op == Cut || op == Copy) {
4278 /* This operation will involve putting things in the cut buffer, so create an empty
4279 ControlList for each of our source lists to put the cut buffer data in.
4281 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4282 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4285 /* Add all selected points to the relevant copy ControlLists */
4286 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4287 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4288 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4289 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4291 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4293 /* Update earliest MIDI start time in beats */
4294 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4296 /* Update earliest session start time in samples */
4297 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4301 /* Snap start time backwards, so copy/paste is snap aligned. */
4303 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4304 earliest = Temporal::Beats(); // Weird... don't offset
4306 earliest.round_down_to_beat();
4308 if (start.sample == std::numeric_limits<double>::max()) {
4309 start.sample = 0; // Weird... don't offset
4311 snap_to(start, RoundDownMaybe);
4314 const double line_offset = midi ? earliest.to_double() : start.sample;
4315 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4316 /* Correct this copy list so that it is relative to the earliest
4317 start time, so relative ordering between points is preserved
4318 when copying from several lists and the paste starts at the
4319 earliest copied piece of data. */
4320 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4321 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4322 (*ctrl_evt)->when -= line_offset;
4325 /* And add it to the cut buffer */
4326 cut_buffer->add (al_cpy);
4330 if (op == Delete || op == Cut) {
4331 /* This operation needs to remove things from the main AutomationList, so do that now */
4333 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4334 i->first->freeze ();
4337 /* Remove each selected point from its AutomationList */
4338 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4339 AutomationLine& line = (*sel_point)->line ();
4340 boost::shared_ptr<AutomationList> al = line.the_list();
4344 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4345 /* removing of first and last gain point in region gain lines is prohibited*/
4346 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4352 al->erase ((*sel_point)->model ());
4356 /* Thaw the lists and add undo records for them */
4357 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4358 boost::shared_ptr<AutomationList> al = i->first;
4360 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4365 /** Cut, copy or clear selected automation points.
4366 * @param op Operation (Cut, Copy or Clear)
4369 Editor::cut_copy_midi (CutCopyOp op)
4371 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4372 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4375 if (!mrv->selection().empty()) {
4376 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4378 mrv->cut_copy_clear (op);
4380 /* XXX: not ideal, as there may be more than one track involved in the selection */
4381 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4385 if (!selection->points.empty()) {
4386 cut_copy_points (op, earliest, true);
4387 if (op == Cut || op == Delete) {
4388 selection->clear_points ();
4393 struct lt_playlist {
4394 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4395 return a.playlist < b.playlist;
4399 struct PlaylistMapping {
4401 boost::shared_ptr<Playlist> pl;
4403 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4406 /** Remove `clicked_regionview' */
4408 Editor::remove_clicked_region ()
4410 if (clicked_routeview == 0 || clicked_regionview == 0) {
4414 begin_reversible_command (_("remove region"));
4416 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4418 playlist->clear_changes ();
4419 playlist->clear_owned_changes ();
4420 playlist->remove_region (clicked_regionview->region());
4421 if (Config->get_edit_mode() == Ripple)
4422 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4424 /* We might have removed regions, which alters other regions' layering_index,
4425 so we need to do a recursive diff here.
4427 vector<Command*> cmds;
4428 playlist->rdiff (cmds);
4429 _session->add_commands (cmds);
4431 _session->add_command(new StatefulDiffCommand (playlist));
4432 commit_reversible_command ();
4436 /** Remove the selected regions */
4438 Editor::remove_selected_regions ()
4440 RegionSelection rs = get_regions_from_selection_and_entered ();
4442 if (!_session || rs.empty()) {
4446 list<boost::shared_ptr<Region> > regions_to_remove;
4448 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4449 // we can't just remove the region(s) in this loop because
4450 // this removes them from the RegionSelection, and they thus
4451 // disappear from underneath the iterator, and the ++i above
4452 // SEGVs in a puzzling fashion.
4454 // so, first iterate over the regions to be removed from rs and
4455 // add them to the regions_to_remove list, and then
4456 // iterate over the list to actually remove them.
4458 regions_to_remove.push_back ((*i)->region());
4461 vector<boost::shared_ptr<Playlist> > playlists;
4463 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4465 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4468 // is this check necessary?
4472 /* get_regions_from_selection_and_entered() guarantees that
4473 the playlists involved are unique, so there is no need
4477 playlists.push_back (playlist);
4479 playlist->clear_changes ();
4480 playlist->clear_owned_changes ();
4481 playlist->freeze ();
4482 playlist->remove_region (*rl);
4483 if (Config->get_edit_mode() == Ripple)
4484 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4488 vector<boost::shared_ptr<Playlist> >::iterator pl;
4489 bool in_command = false;
4491 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4494 /* We might have removed regions, which alters other regions' layering_index,
4495 so we need to do a recursive diff here.
4499 begin_reversible_command (_("remove region"));
4502 vector<Command*> cmds;
4503 (*pl)->rdiff (cmds);
4504 _session->add_commands (cmds);
4506 _session->add_command(new StatefulDiffCommand (*pl));
4510 commit_reversible_command ();
4514 /** Cut, copy or clear selected regions.
4515 * @param op Operation (Cut, Copy or Clear)
4518 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4520 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4521 a map when we want ordered access to both elements. i think.
4524 vector<PlaylistMapping> pmap;
4526 samplepos_t first_position = max_samplepos;
4528 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4529 FreezeList freezelist;
4531 /* get ordering correct before we cut/copy */
4533 rs.sort_by_position_and_track ();
4535 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4537 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4539 if (op == Cut || op == Clear || op == Delete) {
4540 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4543 FreezeList::iterator fl;
4545 // only take state if this is a new playlist.
4546 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4552 if (fl == freezelist.end()) {
4553 pl->clear_changes();
4554 pl->clear_owned_changes ();
4556 freezelist.insert (pl);
4561 TimeAxisView* tv = &(*x)->get_time_axis_view();
4562 vector<PlaylistMapping>::iterator z;
4564 for (z = pmap.begin(); z != pmap.end(); ++z) {
4565 if ((*z).tv == tv) {
4570 if (z == pmap.end()) {
4571 pmap.push_back (PlaylistMapping (tv));
4575 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4577 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4580 /* region not yet associated with a playlist (e.g. unfinished
4587 TimeAxisView& tv = (*x)->get_time_axis_view();
4588 boost::shared_ptr<Playlist> npl;
4589 RegionSelection::iterator tmp;
4596 vector<PlaylistMapping>::iterator z;
4598 for (z = pmap.begin(); z != pmap.end(); ++z) {
4599 if ((*z).tv == &tv) {
4604 assert (z != pmap.end());
4607 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4615 boost::shared_ptr<Region> r = (*x)->region();
4616 boost::shared_ptr<Region> _xx;
4622 pl->remove_region (r);
4623 if (Config->get_edit_mode() == Ripple)
4624 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4628 _xx = RegionFactory::create (r);
4629 npl->add_region (_xx, r->position() - first_position);
4630 pl->remove_region (r);
4631 if (Config->get_edit_mode() == Ripple)
4632 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4636 /* copy region before adding, so we're not putting same object into two different playlists */
4637 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4641 pl->remove_region (r);
4642 if (Config->get_edit_mode() == Ripple)
4643 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4652 list<boost::shared_ptr<Playlist> > foo;
4654 /* the pmap is in the same order as the tracks in which selected regions occurred */
4656 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4659 foo.push_back ((*i).pl);
4664 cut_buffer->set (foo);
4668 _last_cut_copy_source_track = 0;
4670 _last_cut_copy_source_track = pmap.front().tv;
4674 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4677 /* We might have removed regions, which alters other regions' layering_index,
4678 so we need to do a recursive diff here.
4680 vector<Command*> cmds;
4681 (*pl)->rdiff (cmds);
4682 _session->add_commands (cmds);
4684 _session->add_command (new StatefulDiffCommand (*pl));
4689 Editor::cut_copy_ranges (CutCopyOp op)
4691 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4693 /* Sort the track selection now, so that it if is used, the playlists
4694 selected by the calls below to cut_copy_clear are in the order that
4695 their tracks appear in the editor. This makes things like paste
4696 of ranges work properly.
4699 sort_track_selection (ts);
4702 if (!entered_track) {
4705 ts.push_back (entered_track);
4708 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4709 (*i)->cut_copy_clear (*selection, op);
4714 Editor::paste (float times, bool from_context)
4716 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4717 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4718 paste_internal (where.sample, times, 0);
4722 Editor::mouse_paste ()
4724 MusicSample where (0, 0);
4726 if (!mouse_sample (where.sample, ignored)) {
4731 paste_internal (where.sample, 1, where.division);
4735 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4737 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4739 if (cut_buffer->empty(internal_editing())) {
4743 if (position == max_samplepos) {
4744 position = get_preferred_edit_position();
4745 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4748 if (position == last_paste_pos) {
4749 /* repeated paste in the same position */
4752 /* paste in new location, reset repeated paste state */
4754 last_paste_pos = position;
4757 /* get everything in the correct order */
4760 if (!selection->tracks.empty()) {
4761 /* If there is a track selection, paste into exactly those tracks and
4762 * only those tracks. This allows the user to be explicit and override
4763 * the below "do the reasonable thing" logic. */
4764 ts = selection->tracks.filter_to_unique_playlists ();
4765 sort_track_selection (ts);
4767 /* Figure out which track to base the paste at. */
4768 TimeAxisView* base_track = NULL;
4769 if (_edit_point == Editing::EditAtMouse && entered_track) {
4770 /* With the mouse edit point, paste onto the track under the mouse. */
4771 base_track = entered_track;
4772 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4773 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4774 base_track = &entered_regionview->get_time_axis_view();
4775 } else if (_last_cut_copy_source_track) {
4776 /* Paste to the track that the cut/copy came from (see mantis #333). */
4777 base_track = _last_cut_copy_source_track;
4779 /* This is "impossible" since we've copied... well, do nothing. */
4783 /* Walk up to parent if necessary, so base track is a route. */
4784 while (base_track->get_parent()) {
4785 base_track = base_track->get_parent();
4788 /* Add base track and all tracks below it. The paste logic will select
4789 the appropriate object types from the cut buffer in relative order. */
4790 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4791 if ((*i)->order() >= base_track->order()) {
4796 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4797 sort_track_selection (ts);
4799 /* Add automation children of each track in order, for pasting several lines. */
4800 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4801 /* Add any automation children for pasting several lines */
4802 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4807 typedef RouteTimeAxisView::AutomationTracks ATracks;
4808 const ATracks& atracks = rtv->automation_tracks();
4809 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4810 i = ts.insert(i, a->second.get());
4815 /* We now have a list of trackviews starting at base_track, including
4816 automation children, in the order shown in the editor, e.g. R1,
4817 R1.A1, R1.A2, R2, R2.A1, ... */
4820 begin_reversible_command (Operations::paste);
4822 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4823 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4824 /* Only one line copied, and one automation track selected. Do a
4825 "greedy" paste from one automation type to another. */
4827 PasteContext ctx(paste_count, times, ItemCounts(), true);
4828 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4832 /* Paste into tracks */
4834 PasteContext ctx(paste_count, times, ItemCounts(), false);
4835 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4836 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4840 commit_reversible_command ();
4844 Editor::duplicate_regions (float times)
4846 RegionSelection rs (get_regions_from_selection_and_entered());
4847 duplicate_some_regions (rs, times);
4851 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4853 if (regions.empty ()) {
4857 boost::shared_ptr<Playlist> playlist;
4858 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4859 RegionSelection foo;
4861 samplepos_t const start_sample = regions.start ();
4862 samplepos_t const end_sample = regions.end_sample ();
4863 samplecnt_t const gap = end_sample - start_sample + 1;
4865 begin_reversible_command (Operations::duplicate_region);
4867 selection->clear_regions ();
4869 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4871 boost::shared_ptr<Region> r ((*i)->region());
4873 TimeAxisView& tv = (*i)->get_time_axis_view();
4874 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4875 latest_regionviews.clear ();
4876 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4878 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4879 playlist = (*i)->region()->playlist();
4880 playlist->clear_changes ();
4881 playlist->duplicate (r, position, gap, times);
4882 _session->add_command(new StatefulDiffCommand (playlist));
4886 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4890 selection->set (foo);
4893 commit_reversible_command ();
4897 Editor::duplicate_selection (float times)
4899 if (selection->time.empty() || selection->tracks.empty()) {
4903 boost::shared_ptr<Playlist> playlist;
4905 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4907 bool in_command = false;
4909 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4910 if ((playlist = (*i)->playlist()) == 0) {
4913 playlist->clear_changes ();
4915 if (clicked_selection) {
4916 playlist->duplicate_range (selection->time[clicked_selection], times);
4918 playlist->duplicate_ranges (selection->time, times);
4922 begin_reversible_command (_("duplicate range selection"));
4925 _session->add_command (new StatefulDiffCommand (playlist));
4930 if (times == 1.0f) {
4931 // now "move" range selection to after the current range selection
4932 samplecnt_t distance = 0;
4934 if (clicked_selection) {
4936 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4938 distance = selection->time.end_sample () - selection->time.start ();
4941 selection->move_time (distance);
4943 commit_reversible_command ();
4947 /** Reset all selected points to the relevant default value */
4949 Editor::reset_point_selection ()
4951 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4952 ARDOUR::AutomationList::iterator j = (*i)->model ();
4953 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4958 Editor::center_playhead ()
4960 float const page = _visible_canvas_width * samples_per_pixel;
4961 center_screen_internal (playhead_cursor->current_sample (), page);
4965 Editor::center_edit_point ()
4967 float const page = _visible_canvas_width * samples_per_pixel;
4968 center_screen_internal (get_preferred_edit_position(), page);
4971 /** Caller must begin and commit a reversible command */
4973 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4975 playlist->clear_changes ();
4977 _session->add_command (new StatefulDiffCommand (playlist));
4981 Editor::nudge_track (bool use_edit, bool forwards)
4983 boost::shared_ptr<Playlist> playlist;
4984 samplepos_t distance;
4985 samplepos_t next_distance;
4989 start = get_preferred_edit_position();
4994 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4998 if (selection->tracks.empty()) {
5002 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5003 bool in_command = false;
5005 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5007 if ((playlist = (*i)->playlist()) == 0) {
5011 playlist->clear_changes ();
5012 playlist->clear_owned_changes ();
5014 playlist->nudge_after (start, distance, forwards);
5017 begin_reversible_command (_("nudge track"));
5020 vector<Command*> cmds;
5022 playlist->rdiff (cmds);
5023 _session->add_commands (cmds);
5025 _session->add_command (new StatefulDiffCommand (playlist));
5029 commit_reversible_command ();
5034 Editor::remove_last_capture ()
5036 vector<string> choices;
5043 if (Config->get_verify_remove_last_capture()) {
5044 prompt = _("Do you really want to destroy the last capture?"
5045 "\n(This is destructive and cannot be undone)");
5047 choices.push_back (_("No, do nothing."));
5048 choices.push_back (_("Yes, destroy it."));
5050 Choice prompter (_("Destroy last capture"), prompt, choices);
5052 if (prompter.run () == 1) {
5053 _session->remove_last_capture ();
5054 _regions->redisplay ();
5058 _session->remove_last_capture();
5059 _regions->redisplay ();
5064 Editor::normalize_region ()
5070 RegionSelection rs = get_regions_from_selection_and_entered ();
5076 NormalizeDialog dialog (rs.size() > 1);
5078 if (dialog.run () != RESPONSE_ACCEPT) {
5082 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5085 /* XXX: should really only count audio regions here */
5086 int const regions = rs.size ();
5088 /* Make a list of the selected audio regions' maximum amplitudes, and also
5089 obtain the maximum amplitude of them all.
5091 list<double> max_amps;
5092 list<double> rms_vals;
5095 bool use_rms = dialog.constrain_rms ();
5097 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5098 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5102 dialog.descend (1.0 / regions);
5103 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5105 double r = arv->audio_region()->rms (&dialog);
5106 max_rms = max (max_rms, r);
5107 rms_vals.push_back (r);
5111 /* the user cancelled the operation */
5115 max_amps.push_back (a);
5116 max_amp = max (max_amp, a);
5120 list<double>::const_iterator a = max_amps.begin ();
5121 list<double>::const_iterator l = rms_vals.begin ();
5122 bool in_command = false;
5124 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5125 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5130 arv->region()->clear_changes ();
5132 double amp = dialog.normalize_individually() ? *a : max_amp;
5133 double target = dialog.target_peak (); // dB
5136 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5137 const double t_rms = dialog.target_rms ();
5138 const gain_t c_peak = dB_to_coefficient (target);
5139 const gain_t c_rms = dB_to_coefficient (t_rms);
5140 if ((amp_rms / c_rms) > (amp / c_peak)) {
5146 arv->audio_region()->normalize (amp, target);
5149 begin_reversible_command (_("normalize"));
5152 _session->add_command (new StatefulDiffCommand (arv->region()));
5159 commit_reversible_command ();
5165 Editor::reset_region_scale_amplitude ()
5171 RegionSelection rs = get_regions_from_selection_and_entered ();
5177 bool in_command = false;
5179 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5180 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5183 arv->region()->clear_changes ();
5184 arv->audio_region()->set_scale_amplitude (1.0f);
5187 begin_reversible_command ("reset gain");
5190 _session->add_command (new StatefulDiffCommand (arv->region()));
5194 commit_reversible_command ();
5199 Editor::adjust_region_gain (bool up)
5201 RegionSelection rs = get_regions_from_selection_and_entered ();
5203 if (!_session || rs.empty()) {
5207 bool in_command = false;
5209 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5210 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5215 arv->region()->clear_changes ();
5217 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5225 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5228 begin_reversible_command ("adjust region gain");
5231 _session->add_command (new StatefulDiffCommand (arv->region()));
5235 commit_reversible_command ();
5240 Editor::reset_region_gain ()
5242 RegionSelection rs = get_regions_from_selection_and_entered ();
5244 if (!_session || rs.empty()) {
5248 bool in_command = false;
5250 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5251 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5256 arv->region()->clear_changes ();
5258 arv->audio_region()->set_scale_amplitude (1.0f);
5261 begin_reversible_command ("reset region gain");
5264 _session->add_command (new StatefulDiffCommand (arv->region()));
5268 commit_reversible_command ();
5273 Editor::reverse_region ()
5279 Reverse rev (*_session);
5280 apply_filter (rev, _("reverse regions"));
5284 Editor::strip_region_silence ()
5290 RegionSelection rs = get_regions_from_selection_and_entered ();
5296 std::list<RegionView*> audio_only;
5298 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5299 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5301 audio_only.push_back (arv);
5305 assert (!audio_only.empty());
5307 StripSilenceDialog d (_session, audio_only);
5308 int const r = d.run ();
5312 if (r == Gtk::RESPONSE_OK) {
5313 ARDOUR::AudioIntervalMap silences;
5314 d.silences (silences);
5315 StripSilence s (*_session, silences, d.fade_length());
5317 apply_filter (s, _("strip silence"), &d);
5322 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5324 Evoral::Sequence<Temporal::Beats>::Notes selected;
5325 mrv.selection_as_notelist (selected, true);
5327 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5328 v.push_back (selected);
5330 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5332 return op (mrv.midi_region()->model(), pos_beats, v);
5336 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5342 bool in_command = false;
5344 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5345 RegionSelection::const_iterator tmp = r;
5348 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5351 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5354 begin_reversible_command (op.name ());
5358 _session->add_command (cmd);
5366 commit_reversible_command ();
5367 _session->set_dirty ();
5372 Editor::fork_region ()
5374 RegionSelection rs = get_regions_from_selection_and_entered ();
5380 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5381 bool in_command = false;
5385 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5386 RegionSelection::iterator tmp = r;
5389 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5393 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5394 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5395 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5398 begin_reversible_command (_("Fork Region(s)"));
5401 playlist->clear_changes ();
5402 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5403 _session->add_command(new StatefulDiffCommand (playlist));
5405 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5413 commit_reversible_command ();
5418 Editor::quantize_region ()
5421 quantize_regions(get_regions_from_selection_and_entered ());
5426 Editor::quantize_regions (const RegionSelection& rs)
5428 if (rs.n_midi_regions() == 0) {
5432 if (!quantize_dialog) {
5433 quantize_dialog = new QuantizeDialog (*this);
5436 if (quantize_dialog->is_mapped()) {
5437 /* in progress already */
5441 quantize_dialog->present ();
5442 const int r = quantize_dialog->run ();
5443 quantize_dialog->hide ();
5445 if (r == Gtk::RESPONSE_OK) {
5446 Quantize quant (quantize_dialog->snap_start(),
5447 quantize_dialog->snap_end(),
5448 quantize_dialog->start_grid_size(),
5449 quantize_dialog->end_grid_size(),
5450 quantize_dialog->strength(),
5451 quantize_dialog->swing(),
5452 quantize_dialog->threshold());
5454 apply_midi_note_edit_op (quant, rs);
5459 Editor::legatize_region (bool shrink_only)
5462 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5467 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5469 if (rs.n_midi_regions() == 0) {
5473 Legatize legatize(shrink_only);
5474 apply_midi_note_edit_op (legatize, rs);
5478 Editor::transform_region ()
5481 transform_regions(get_regions_from_selection_and_entered ());
5486 Editor::transform_regions (const RegionSelection& rs)
5488 if (rs.n_midi_regions() == 0) {
5495 const int r = td.run();
5498 if (r == Gtk::RESPONSE_OK) {
5499 Transform transform(td.get());
5500 apply_midi_note_edit_op(transform, rs);
5505 Editor::transpose_region ()
5508 transpose_regions(get_regions_from_selection_and_entered ());
5513 Editor::transpose_regions (const RegionSelection& rs)
5515 if (rs.n_midi_regions() == 0) {
5520 int const r = d.run ();
5522 if (r == RESPONSE_ACCEPT) {
5523 Transpose transpose(d.semitones ());
5524 apply_midi_note_edit_op (transpose, rs);
5529 Editor::insert_patch_change (bool from_context)
5531 RegionSelection rs = get_regions_from_selection_and_entered ();
5537 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5539 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5540 there may be more than one, but the PatchChangeDialog can only offer
5541 one set of patch menus.
5543 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5545 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5546 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5548 if (d.run() == RESPONSE_CANCEL) {
5552 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5553 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5555 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5556 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5563 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5565 RegionSelection rs = get_regions_from_selection_and_entered ();
5571 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5572 bool in_command = false;
5577 int const N = rs.size ();
5579 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5580 RegionSelection::iterator tmp = r;
5583 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5585 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5588 progress->descend (1.0 / N);
5591 if (arv->audio_region()->apply (filter, progress) == 0) {
5593 playlist->clear_changes ();
5594 playlist->clear_owned_changes ();
5597 begin_reversible_command (command);
5601 if (filter.results.empty ()) {
5603 /* no regions returned; remove the old one */
5604 playlist->remove_region (arv->region ());
5608 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5610 /* first region replaces the old one */
5611 playlist->replace_region (arv->region(), *res, (*res)->position());
5615 while (res != filter.results.end()) {
5616 playlist->add_region (*res, (*res)->position());
5622 /* We might have removed regions, which alters other regions' layering_index,
5623 so we need to do a recursive diff here.
5625 vector<Command*> cmds;
5626 playlist->rdiff (cmds);
5627 _session->add_commands (cmds);
5629 _session->add_command(new StatefulDiffCommand (playlist));
5633 progress->ascend ();
5642 commit_reversible_command ();
5647 Editor::external_edit_region ()
5653 Editor::reset_region_gain_envelopes ()
5655 RegionSelection rs = get_regions_from_selection_and_entered ();
5657 if (!_session || rs.empty()) {
5661 bool in_command = false;
5663 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5664 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5666 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5667 XMLNode& before (alist->get_state());
5669 arv->audio_region()->set_default_envelope ();
5672 begin_reversible_command (_("reset region gain"));
5675 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5680 commit_reversible_command ();
5685 Editor::set_region_gain_visibility (RegionView* rv)
5687 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5689 arv->update_envelope_visibility();
5694 Editor::set_gain_envelope_visibility ()
5700 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5701 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5703 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5709 Editor::toggle_gain_envelope_active ()
5711 if (_ignore_region_action) {
5715 RegionSelection rs = get_regions_from_selection_and_entered ();
5717 if (!_session || rs.empty()) {
5721 bool in_command = false;
5723 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5724 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5726 arv->region()->clear_changes ();
5727 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5730 begin_reversible_command (_("region gain envelope active"));
5733 _session->add_command (new StatefulDiffCommand (arv->region()));
5738 commit_reversible_command ();
5743 Editor::toggle_region_lock ()
5745 if (_ignore_region_action) {
5749 RegionSelection rs = get_regions_from_selection_and_entered ();
5751 if (!_session || rs.empty()) {
5755 begin_reversible_command (_("toggle region lock"));
5757 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5758 (*i)->region()->clear_changes ();
5759 (*i)->region()->set_locked (!(*i)->region()->locked());
5760 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5763 commit_reversible_command ();
5767 Editor::toggle_region_video_lock ()
5769 if (_ignore_region_action) {
5773 RegionSelection rs = get_regions_from_selection_and_entered ();
5775 if (!_session || rs.empty()) {
5779 begin_reversible_command (_("Toggle Video Lock"));
5781 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5782 (*i)->region()->clear_changes ();
5783 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5784 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5787 commit_reversible_command ();
5791 Editor::toggle_region_lock_style ()
5793 if (_ignore_region_action) {
5797 RegionSelection rs = get_regions_from_selection_and_entered ();
5799 if (!_session || rs.empty()) {
5803 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5804 vector<Widget*> proxies = a->get_proxies();
5805 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5809 begin_reversible_command (_("toggle region lock style"));
5811 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5812 (*i)->region()->clear_changes ();
5813 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5814 (*i)->region()->set_position_lock_style (ns);
5815 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5818 commit_reversible_command ();
5822 Editor::toggle_opaque_region ()
5824 if (_ignore_region_action) {
5828 RegionSelection rs = get_regions_from_selection_and_entered ();
5830 if (!_session || rs.empty()) {
5834 begin_reversible_command (_("change region opacity"));
5836 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5837 (*i)->region()->clear_changes ();
5838 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5839 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5842 commit_reversible_command ();
5846 Editor::toggle_record_enable ()
5848 bool new_state = false;
5850 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5851 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5854 if (!rtav->is_track())
5858 new_state = !rtav->track()->rec_enable_control()->get_value();
5862 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5867 Editor::toggle_solo ()
5869 bool new_state = false;
5871 boost::shared_ptr<ControlList> cl (new ControlList);
5873 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5874 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5876 if (!stav || !stav->stripable()->solo_control()) {
5881 new_state = !stav->stripable()->solo_control()->soloed ();
5885 cl->push_back (stav->stripable()->solo_control());
5888 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5892 Editor::toggle_mute ()
5894 bool new_state = false;
5896 boost::shared_ptr<ControlList> cl (new ControlList);
5898 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5899 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5901 if (!stav || !stav->stripable()->mute_control()) {
5906 new_state = !stav->stripable()->mute_control()->muted();
5910 cl->push_back (stav->stripable()->mute_control());
5913 _session->set_controls (cl, new_state, Controllable::UseGroup);
5917 Editor::toggle_solo_isolate ()
5923 Editor::fade_range ()
5925 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5927 begin_reversible_command (_("fade range"));
5929 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5930 (*i)->fade_range (selection->time);
5933 commit_reversible_command ();
5938 Editor::set_fade_length (bool in)
5940 RegionSelection rs = get_regions_from_selection_and_entered ();
5946 /* we need a region to measure the offset from the start */
5948 RegionView* rv = rs.front ();
5950 samplepos_t pos = get_preferred_edit_position();
5954 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
5955 /* edit point is outside the relevant region */
5960 if (pos <= rv->region()->position()) {
5964 len = pos - rv->region()->position();
5965 cmd = _("set fade in length");
5967 if (pos >= rv->region()->last_sample()) {
5971 len = rv->region()->last_sample() - pos;
5972 cmd = _("set fade out length");
5975 bool in_command = false;
5977 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5978 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5984 boost::shared_ptr<AutomationList> alist;
5986 alist = tmp->audio_region()->fade_in();
5988 alist = tmp->audio_region()->fade_out();
5991 XMLNode &before = alist->get_state();
5994 tmp->audio_region()->set_fade_in_length (len);
5995 tmp->audio_region()->set_fade_in_active (true);
5997 tmp->audio_region()->set_fade_out_length (len);
5998 tmp->audio_region()->set_fade_out_active (true);
6002 begin_reversible_command (cmd);
6005 XMLNode &after = alist->get_state();
6006 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6010 commit_reversible_command ();
6015 Editor::set_fade_in_shape (FadeShape shape)
6017 RegionSelection rs = get_regions_from_selection_and_entered ();
6022 bool in_command = false;
6024 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6025 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6031 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6032 XMLNode &before = alist->get_state();
6034 tmp->audio_region()->set_fade_in_shape (shape);
6037 begin_reversible_command (_("set fade in shape"));
6040 XMLNode &after = alist->get_state();
6041 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6045 commit_reversible_command ();
6050 Editor::set_fade_out_shape (FadeShape shape)
6052 RegionSelection rs = get_regions_from_selection_and_entered ();
6057 bool in_command = false;
6059 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6060 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6066 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6067 XMLNode &before = alist->get_state();
6069 tmp->audio_region()->set_fade_out_shape (shape);
6072 begin_reversible_command (_("set fade out shape"));
6075 XMLNode &after = alist->get_state();
6076 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6080 commit_reversible_command ();
6085 Editor::set_fade_in_active (bool yn)
6087 RegionSelection rs = get_regions_from_selection_and_entered ();
6092 bool in_command = false;
6094 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6095 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6102 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6104 ar->clear_changes ();
6105 ar->set_fade_in_active (yn);
6108 begin_reversible_command (_("set fade in active"));
6111 _session->add_command (new StatefulDiffCommand (ar));
6115 commit_reversible_command ();
6120 Editor::set_fade_out_active (bool yn)
6122 RegionSelection rs = get_regions_from_selection_and_entered ();
6127 bool in_command = false;
6129 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6130 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6136 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6138 ar->clear_changes ();
6139 ar->set_fade_out_active (yn);
6142 begin_reversible_command (_("set fade out active"));
6145 _session->add_command(new StatefulDiffCommand (ar));
6149 commit_reversible_command ();
6154 Editor::toggle_region_fades (int dir)
6156 if (_ignore_region_action) {
6160 boost::shared_ptr<AudioRegion> ar;
6163 RegionSelection rs = get_regions_from_selection_and_entered ();
6169 RegionSelection::iterator i;
6170 for (i = rs.begin(); i != rs.end(); ++i) {
6171 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6173 yn = ar->fade_out_active ();
6175 yn = ar->fade_in_active ();
6181 if (i == rs.end()) {
6185 /* XXX should this undo-able? */
6186 bool in_command = false;
6188 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6189 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6192 ar->clear_changes ();
6194 if (dir == 1 || dir == 0) {
6195 ar->set_fade_in_active (!yn);
6198 if (dir == -1 || dir == 0) {
6199 ar->set_fade_out_active (!yn);
6202 begin_reversible_command (_("toggle fade active"));
6205 _session->add_command(new StatefulDiffCommand (ar));
6209 commit_reversible_command ();
6214 /** Update region fade visibility after its configuration has been changed */
6216 Editor::update_region_fade_visibility ()
6218 bool _fade_visibility = _session->config.get_show_region_fades ();
6220 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6221 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6223 if (_fade_visibility) {
6224 v->audio_view()->show_all_fades ();
6226 v->audio_view()->hide_all_fades ();
6233 Editor::set_edit_point ()
6236 MusicSample where (0, 0);
6238 if (!mouse_sample (where.sample, ignored)) {
6244 if (selection->markers.empty()) {
6246 mouse_add_new_marker (where.sample);
6251 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6254 loc->move_to (where.sample, where.division);
6260 Editor::set_playhead_cursor ()
6262 if (entered_marker) {
6263 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6265 MusicSample where (0, 0);
6268 if (!mouse_sample (where.sample, ignored)) {
6275 _session->request_locate (where.sample, _session->transport_rolling());
6279 //not sure what this was for; remove it for now.
6280 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6281 // cancel_time_selection();
6287 Editor::split_region ()
6289 if (_drags->active ()) {
6293 //if a range is selected, separate it
6294 if (!selection->time.empty()) {
6295 separate_regions_between (selection->time);
6299 //if no range was selected, try to find some regions to split
6300 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6302 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6303 const samplepos_t pos = get_preferred_edit_position();
6304 const int32_t division = get_grid_music_divisions (0);
6305 MusicSample where (pos, division);
6311 split_regions_at (where, rs);
6317 Editor::select_next_stripable (bool routes_only)
6319 if (selection->tracks.empty()) {
6320 selection->set (track_views.front());
6324 TimeAxisView* current = selection->tracks.front();
6328 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6330 if (*i == current) {
6332 if (i != track_views.end()) {
6335 current = (*(track_views.begin()));
6336 //selection->set (*(track_views.begin()));
6343 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6344 valid = rui && rui->route()->active();
6346 valid = 0 != current->stripable ().get();
6349 } while (current->hidden() || !valid);
6351 selection->set (current);
6353 ensure_time_axis_view_is_visible (*current, false);
6357 Editor::select_prev_stripable (bool routes_only)
6359 if (selection->tracks.empty()) {
6360 selection->set (track_views.front());
6364 TimeAxisView* current = selection->tracks.front();
6368 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6370 if (*i == current) {
6372 if (i != track_views.rend()) {
6375 current = *(track_views.rbegin());
6381 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6382 valid = rui && rui->route()->active();
6384 valid = 0 != current->stripable ().get();
6387 } while (current->hidden() || !valid);
6389 selection->set (current);
6391 ensure_time_axis_view_is_visible (*current, false);
6395 Editor::set_loop_from_selection (bool play)
6397 if (_session == 0) {
6401 samplepos_t start, end;
6402 if (!get_selection_extents (start, end))
6405 set_loop_range (start, end, _("set loop range from selection"));
6408 _session->request_play_loop (true, true);
6413 Editor::set_loop_from_region (bool play)
6415 samplepos_t start, end;
6416 if (!get_selection_extents (start, end))
6419 set_loop_range (start, end, _("set loop range from region"));
6422 _session->request_locate (start, true);
6423 _session->request_play_loop (true);
6428 Editor::set_punch_from_selection ()
6430 if (_session == 0) {
6434 samplepos_t start, end;
6435 if (!get_selection_extents (start, end))
6438 set_punch_range (start, end, _("set punch range from selection"));
6442 Editor::set_auto_punch_range ()
6444 // auto punch in/out button from a single button
6445 // If Punch In is unset, set punch range from playhead to end, enable punch in
6446 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6447 // rewound beyond the Punch In marker, in which case that marker will be moved back
6448 // to the current playhead position.
6449 // If punch out is set, it clears the punch range and Punch In/Out buttons
6451 if (_session == 0) {
6455 Location* tpl = transport_punch_location();
6456 samplepos_t now = playhead_cursor->current_sample();
6457 samplepos_t begin = now;
6458 samplepos_t end = _session->current_end_sample();
6460 if (!_session->config.get_punch_in()) {
6461 // First Press - set punch in and create range from here to eternity
6462 set_punch_range (begin, end, _("Auto Punch In"));
6463 _session->config.set_punch_in(true);
6464 } else if (tpl && !_session->config.get_punch_out()) {
6465 // Second press - update end range marker and set punch_out
6466 if (now < tpl->start()) {
6467 // playhead has been rewound - move start back and pretend nothing happened
6469 set_punch_range (begin, end, _("Auto Punch In/Out"));
6471 // normal case for 2nd press - set the punch out
6472 end = playhead_cursor->current_sample ();
6473 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6474 _session->config.set_punch_out(true);
6477 if (_session->config.get_punch_out()) {
6478 _session->config.set_punch_out(false);
6481 if (_session->config.get_punch_in()) {
6482 _session->config.set_punch_in(false);
6487 // third press - unset punch in/out and remove range
6488 _session->locations()->remove(tpl);
6495 Editor::set_session_extents_from_selection ()
6497 if (_session == 0) {
6501 samplepos_t start, end;
6502 if (!get_selection_extents (start, end))
6506 if ((loc = _session->locations()->session_range_location()) == 0) {
6507 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6509 XMLNode &before = loc->get_state();
6511 _session->set_session_extents (start, end);
6513 XMLNode &after = loc->get_state();
6515 begin_reversible_command (_("set session start/end from selection"));
6517 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6519 commit_reversible_command ();
6522 _session->set_end_is_free (false);
6526 Editor::set_punch_start_from_edit_point ()
6530 MusicSample start (0, 0);
6531 samplepos_t end = max_samplepos;
6533 //use the existing punch end, if any
6534 Location* tpl = transport_punch_location();
6539 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6540 start.sample = _session->audible_sample();
6542 start.sample = get_preferred_edit_position();
6545 //snap the selection start/end
6548 //if there's not already a sensible selection endpoint, go "forever"
6549 if (start.sample > end ) {
6550 end = max_samplepos;
6553 set_punch_range (start.sample, end, _("set punch start from EP"));
6559 Editor::set_punch_end_from_edit_point ()
6563 samplepos_t start = 0;
6564 MusicSample end (max_samplepos, 0);
6566 //use the existing punch start, if any
6567 Location* tpl = transport_punch_location();
6569 start = tpl->start();
6572 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6573 end.sample = _session->audible_sample();
6575 end.sample = get_preferred_edit_position();
6578 //snap the selection start/end
6581 set_punch_range (start, end.sample, _("set punch end from EP"));
6587 Editor::set_loop_start_from_edit_point ()
6591 MusicSample start (0, 0);
6592 samplepos_t end = max_samplepos;
6594 //use the existing loop end, if any
6595 Location* tpl = transport_loop_location();
6600 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6601 start.sample = _session->audible_sample();
6603 start.sample = get_preferred_edit_position();
6606 //snap the selection start/end
6609 //if there's not already a sensible selection endpoint, go "forever"
6610 if (start.sample > end ) {
6611 end = max_samplepos;
6614 set_loop_range (start.sample, end, _("set loop start from EP"));
6620 Editor::set_loop_end_from_edit_point ()
6624 samplepos_t start = 0;
6625 MusicSample end (max_samplepos, 0);
6627 //use the existing loop start, if any
6628 Location* tpl = transport_loop_location();
6630 start = tpl->start();
6633 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6634 end.sample = _session->audible_sample();
6636 end.sample = get_preferred_edit_position();
6639 //snap the selection start/end
6642 set_loop_range (start, end.sample, _("set loop end from EP"));
6647 Editor::set_punch_from_region ()
6649 samplepos_t start, end;
6650 if (!get_selection_extents (start, end))
6653 set_punch_range (start, end, _("set punch range from region"));
6657 Editor::pitch_shift_region ()
6659 RegionSelection rs = get_regions_from_selection_and_entered ();
6661 RegionSelection audio_rs;
6662 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6663 if (dynamic_cast<AudioRegionView*> (*i)) {
6664 audio_rs.push_back (*i);
6668 if (audio_rs.empty()) {
6672 pitch_shift (audio_rs, 1.2);
6676 Editor::set_tempo_from_region ()
6678 RegionSelection rs = get_regions_from_selection_and_entered ();
6680 if (!_session || rs.empty()) {
6684 RegionView* rv = rs.front();
6686 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6690 Editor::use_range_as_bar ()
6692 samplepos_t start, end;
6693 if (get_edit_op_range (start, end)) {
6694 define_one_bar (start, end);
6699 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6701 samplepos_t length = end - start;
6703 const Meter& m (_session->tempo_map().meter_at_sample (start));
6705 /* length = 1 bar */
6707 /* We're going to deliver a constant tempo here,
6708 so we can use samples per beat to determine length.
6709 now we want samples per beat.
6710 we have samples per bar, and beats per bar, so ...
6713 /* XXXX METER MATH */
6715 double samples_per_beat = length / m.divisions_per_bar();
6717 /* beats per minute = */
6719 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6721 /* now decide whether to:
6723 (a) set global tempo
6724 (b) add a new tempo marker
6728 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6730 bool do_global = false;
6732 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6734 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6735 at the start, or create a new marker
6738 vector<string> options;
6739 options.push_back (_("Cancel"));
6740 options.push_back (_("Add new marker"));
6741 options.push_back (_("Set global tempo"));
6744 _("Define one bar"),
6745 _("Do you want to set the global tempo or add a new tempo marker?"),
6749 c.set_default_response (2);
6765 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6766 if the marker is at the region starter, change it, otherwise add
6771 begin_reversible_command (_("set tempo from region"));
6772 XMLNode& before (_session->tempo_map().get_state());
6775 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6776 } else if (t.sample() == start) {
6777 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6779 /* constant tempo */
6780 const Tempo tempo (beats_per_minute, t.note_type());
6781 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6784 XMLNode& after (_session->tempo_map().get_state());
6786 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6787 commit_reversible_command ();
6791 Editor::split_region_at_transients ()
6793 AnalysisFeatureList positions;
6795 RegionSelection rs = get_regions_from_selection_and_entered ();
6797 if (!_session || rs.empty()) {
6801 begin_reversible_command (_("split regions"));
6803 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6805 RegionSelection::iterator tmp;
6810 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6813 ar->transients (positions);
6814 split_region_at_points ((*i)->region(), positions, true);
6821 commit_reversible_command ();
6826 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6828 bool use_rhythmic_rodent = false;
6830 boost::shared_ptr<Playlist> pl = r->playlist();
6832 list<boost::shared_ptr<Region> > new_regions;
6838 if (positions.empty()) {
6842 if (positions.size() > 20 && can_ferret) {
6843 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);
6844 MessageDialog msg (msgstr,
6847 Gtk::BUTTONS_OK_CANCEL);
6850 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6851 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6853 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6856 msg.set_title (_("Excessive split?"));
6859 int response = msg.run();
6865 case RESPONSE_APPLY:
6866 use_rhythmic_rodent = true;
6873 if (use_rhythmic_rodent) {
6874 show_rhythm_ferret ();
6878 AnalysisFeatureList::const_iterator x;
6880 pl->clear_changes ();
6881 pl->clear_owned_changes ();
6883 x = positions.begin();
6885 if (x == positions.end()) {
6890 pl->remove_region (r);
6892 samplepos_t pos = 0;
6894 samplepos_t rstart = r->first_sample ();
6895 samplepos_t rend = r->last_sample ();
6897 while (x != positions.end()) {
6899 /* deal with positons that are out of scope of present region bounds */
6900 if (*x <= rstart || *x > rend) {
6905 /* file start = original start + how far we from the initial position ? */
6907 samplepos_t file_start = r->start() + pos;
6909 /* length = next position - current position */
6911 samplepos_t len = (*x) - pos - rstart;
6913 /* XXX we do we really want to allow even single-sample regions?
6914 * shouldn't we have some kind of lower limit on region size?
6923 if (RegionFactory::region_name (new_name, r->name())) {
6927 /* do NOT announce new regions 1 by one, just wait till they are all done */
6931 plist.add (ARDOUR::Properties::start, file_start);
6932 plist.add (ARDOUR::Properties::length, len);
6933 plist.add (ARDOUR::Properties::name, new_name);
6934 plist.add (ARDOUR::Properties::layer, 0);
6935 // TODO set transients_offset
6937 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6938 /* because we set annouce to false, manually add the new region to the
6941 RegionFactory::map_add (nr);
6943 pl->add_region (nr, rstart + pos);
6946 new_regions.push_front(nr);
6955 RegionFactory::region_name (new_name, r->name());
6957 /* Add the final region */
6960 plist.add (ARDOUR::Properties::start, r->start() + pos);
6961 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6962 plist.add (ARDOUR::Properties::name, new_name);
6963 plist.add (ARDOUR::Properties::layer, 0);
6965 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6966 /* because we set annouce to false, manually add the new region to the
6969 RegionFactory::map_add (nr);
6970 pl->add_region (nr, r->position() + pos);
6973 new_regions.push_front(nr);
6978 /* We might have removed regions, which alters other regions' layering_index,
6979 so we need to do a recursive diff here.
6981 vector<Command*> cmds;
6983 _session->add_commands (cmds);
6985 _session->add_command (new StatefulDiffCommand (pl));
6989 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6990 set_selected_regionview_from_region_list ((*i), Selection::Add);
6996 Editor::place_transient()
7002 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7008 samplepos_t where = get_preferred_edit_position();
7010 begin_reversible_command (_("place transient"));
7012 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7013 (*r)->region()->add_transient(where);
7016 commit_reversible_command ();
7020 Editor::remove_transient(ArdourCanvas::Item* item)
7026 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7029 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7030 _arv->remove_transient (*(float*) _line->get_data ("position"));
7034 Editor::snap_regions_to_grid ()
7036 list <boost::shared_ptr<Playlist > > used_playlists;
7038 RegionSelection rs = get_regions_from_selection_and_entered ();
7040 if (!_session || rs.empty()) {
7044 begin_reversible_command (_("snap regions to grid"));
7046 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7048 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7050 if (!pl->frozen()) {
7051 /* we haven't seen this playlist before */
7053 /* remember used playlists so we can thaw them later */
7054 used_playlists.push_back(pl);
7057 (*r)->region()->clear_changes ();
7059 MusicSample start ((*r)->region()->first_sample (), 0);
7061 (*r)->region()->set_position (start.sample, start.division);
7062 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7065 while (used_playlists.size() > 0) {
7066 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7068 used_playlists.pop_front();
7071 commit_reversible_command ();
7075 Editor::close_region_gaps ()
7077 list <boost::shared_ptr<Playlist > > used_playlists;
7079 RegionSelection rs = get_regions_from_selection_and_entered ();
7081 if (!_session || rs.empty()) {
7085 Dialog dialog (_("Close Region Gaps"));
7088 table.set_spacings (12);
7089 table.set_border_width (12);
7090 Label* l = manage (left_aligned_label (_("Crossfade length")));
7091 table.attach (*l, 0, 1, 0, 1);
7093 SpinButton spin_crossfade (1, 0);
7094 spin_crossfade.set_range (0, 15);
7095 spin_crossfade.set_increments (1, 1);
7096 spin_crossfade.set_value (5);
7097 table.attach (spin_crossfade, 1, 2, 0, 1);
7099 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7101 l = manage (left_aligned_label (_("Pull-back length")));
7102 table.attach (*l, 0, 1, 1, 2);
7104 SpinButton spin_pullback (1, 0);
7105 spin_pullback.set_range (0, 100);
7106 spin_pullback.set_increments (1, 1);
7107 spin_pullback.set_value(30);
7108 table.attach (spin_pullback, 1, 2, 1, 2);
7110 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7112 dialog.get_vbox()->pack_start (table);
7113 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7114 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7117 if (dialog.run () == RESPONSE_CANCEL) {
7121 samplepos_t crossfade_len = spin_crossfade.get_value();
7122 samplepos_t pull_back_samples = spin_pullback.get_value();
7124 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7125 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7127 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7129 begin_reversible_command (_("close region gaps"));
7132 boost::shared_ptr<Region> last_region;
7134 rs.sort_by_position_and_track();
7136 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7138 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7140 if (!pl->frozen()) {
7141 /* we haven't seen this playlist before */
7143 /* remember used playlists so we can thaw them later */
7144 used_playlists.push_back(pl);
7148 samplepos_t position = (*r)->region()->position();
7150 if (idx == 0 || position < last_region->position()){
7151 last_region = (*r)->region();
7156 (*r)->region()->clear_changes ();
7157 (*r)->region()->trim_front((position - pull_back_samples));
7159 last_region->clear_changes ();
7160 last_region->trim_end ((position - pull_back_samples + crossfade_len));
7162 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7163 _session->add_command (new StatefulDiffCommand (last_region));
7165 last_region = (*r)->region();
7169 while (used_playlists.size() > 0) {
7170 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7172 used_playlists.pop_front();
7175 commit_reversible_command ();
7179 Editor::tab_to_transient (bool forward)
7181 AnalysisFeatureList positions;
7183 RegionSelection rs = get_regions_from_selection_and_entered ();
7189 samplepos_t pos = _session->audible_sample ();
7191 if (!selection->tracks.empty()) {
7193 /* don't waste time searching for transients in duplicate playlists.
7196 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7198 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7200 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7203 boost::shared_ptr<Track> tr = rtv->track();
7205 boost::shared_ptr<Playlist> pl = tr->playlist ();
7207 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7210 positions.push_back (result);
7223 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7224 (*r)->region()->get_transients (positions);
7228 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7231 AnalysisFeatureList::iterator x;
7233 for (x = positions.begin(); x != positions.end(); ++x) {
7239 if (x != positions.end ()) {
7240 _session->request_locate (*x);
7244 AnalysisFeatureList::reverse_iterator x;
7246 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7252 if (x != positions.rend ()) {
7253 _session->request_locate (*x);
7259 Editor::playhead_forward_to_grid ()
7265 MusicSample pos (playhead_cursor->current_sample (), 0);
7267 if (pos.sample < max_samplepos - 1) {
7269 snap_to_internal (pos, RoundUpAlways, false, true);
7270 _session->request_locate (pos.sample);
7276 Editor::playhead_backward_to_grid ()
7282 MusicSample pos (playhead_cursor->current_sample (), 0);
7284 if (pos.sample > 2) {
7286 snap_to_internal (pos, RoundDownAlways, false, true);
7287 _session->request_locate (pos.sample);
7292 Editor::set_track_height (Height h)
7294 TrackSelection& ts (selection->tracks);
7296 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7297 (*x)->set_height_enum (h);
7302 Editor::toggle_tracks_active ()
7304 TrackSelection& ts (selection->tracks);
7306 bool target = false;
7312 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7313 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7317 target = !rtv->_route->active();
7320 rtv->_route->set_active (target, this);
7326 Editor::remove_tracks ()
7328 /* this will delete GUI objects that may be the subject of an event
7329 handler in which this method is called. Defer actual deletion to the
7330 next idle callback, when all event handling is finished.
7332 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7336 Editor::idle_remove_tracks ()
7338 Session::StateProtector sp (_session);
7340 return false; /* do not call again */
7344 Editor::_remove_tracks ()
7346 TrackSelection& ts (selection->tracks);
7352 vector<string> choices;
7357 const char* trackstr;
7360 vector<boost::shared_ptr<Route> > routes;
7361 vector<boost::shared_ptr<VCA> > vcas;
7362 bool special_bus = false;
7364 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7365 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7367 vcas.push_back (vtv->vca());
7371 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7375 if (rtv->is_track()) {
7380 routes.push_back (rtv->_route);
7382 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7387 if (special_bus && !Config->get_allow_special_bus_removal()) {
7388 MessageDialog msg (_("That would be bad news ...."),
7392 msg.set_secondary_text (string_compose (_(
7393 "Removing the master or monitor bus is such a bad idea\n\
7394 that %1 is not going to allow it.\n\
7396 If you really want to do this sort of thing\n\
7397 edit your ardour.rc file to set the\n\
7398 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7405 if (ntracks + nbusses + nvcas == 0) {
7411 trackstr = P_("track", "tracks", ntracks);
7412 busstr = P_("bus", "busses", nbusses);
7413 vcastr = P_("VCA", "VCAs", nvcas);
7415 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7416 title = _("Remove various strips");
7417 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7418 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7420 else if (ntracks > 0 && nbusses > 0) {
7421 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7422 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7423 ntracks, trackstr, nbusses, busstr);
7425 else if (ntracks > 0 && nvcas > 0) {
7426 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7427 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7428 ntracks, trackstr, nvcas, vcastr);
7430 else if (nbusses > 0 && nvcas > 0) {
7431 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7432 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7433 nbusses, busstr, nvcas, vcastr);
7435 else if (ntracks > 0) {
7436 title = string_compose (_("Remove %1"), trackstr);
7437 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7440 else if (nbusses > 0) {
7441 title = string_compose (_("Remove %1"), busstr);
7442 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7445 else if (nvcas > 0) {
7446 title = string_compose (_("Remove %1"), vcastr);
7447 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7455 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7458 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7460 choices.push_back (_("No, do nothing."));
7461 if (ntracks + nbusses + nvcas > 1) {
7462 choices.push_back (_("Yes, remove them."));
7464 choices.push_back (_("Yes, remove it."));
7467 Choice prompter (title, prompt, choices);
7469 if (prompter.run () != 1) {
7473 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7474 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7475 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7476 * likely because deletion requires selection) this will call
7477 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7478 * It's likewise likely that the route that has just been displayed in the
7479 * Editor-Mixer will be next in line for deletion.
7481 * So simply switch to the master-bus (if present)
7483 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7484 if ((*i)->stripable ()->is_master ()) {
7485 set_selected_mixer_strip (*(*i));
7492 PresentationInfo::ChangeSuspender cs;
7493 DisplaySuspender ds;
7495 boost::shared_ptr<RouteList> rl (new RouteList);
7496 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7499 _session->remove_routes (rl);
7501 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7502 _session->vca_manager().remove_vca (*x);
7506 /* TrackSelection and RouteList leave scope,
7507 * destructors are called,
7508 * diskstream drops references, save_state is called (again for every track)
7513 Editor::do_insert_time ()
7515 if (selection->tracks.empty()) {
7519 InsertRemoveTimeDialog d (*this);
7520 int response = d.run ();
7522 if (response != RESPONSE_OK) {
7526 if (d.distance() == 0) {
7533 d.intersected_region_action (),
7537 d.move_glued_markers(),
7538 d.move_locked_markers(),
7544 Editor::insert_time (
7545 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7546 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7550 if (Config->get_edit_mode() == Lock) {
7553 bool in_command = false;
7555 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7557 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7561 /* don't operate on any playlist more than once, which could
7562 * happen if "all playlists" is enabled, but there is more
7563 * than 1 track using playlists "from" a given track.
7566 set<boost::shared_ptr<Playlist> > pl;
7568 if (all_playlists) {
7569 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7570 if (rtav && rtav->track ()) {
7571 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7572 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7577 if ((*x)->playlist ()) {
7578 pl.insert ((*x)->playlist ());
7582 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7584 (*i)->clear_changes ();
7585 (*i)->clear_owned_changes ();
7588 begin_reversible_command (_("insert time"));
7592 if (opt == SplitIntersected) {
7593 /* non musical split */
7594 (*i)->split (MusicSample (pos, 0));
7597 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7599 vector<Command*> cmds;
7601 _session->add_commands (cmds);
7603 _session->add_command (new StatefulDiffCommand (*i));
7607 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7610 begin_reversible_command (_("insert time"));
7613 rtav->route ()->shift (pos, samples);
7620 const int32_t divisions = get_grid_music_divisions (0);
7621 XMLNode& before (_session->locations()->get_state());
7622 Locations::LocationList copy (_session->locations()->list());
7624 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7626 Locations::LocationList::const_iterator tmp;
7628 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7629 bool const was_locked = (*i)->locked ();
7630 if (locked_markers_too) {
7634 if ((*i)->start() >= pos) {
7635 // move end first, in case we're moving by more than the length of the range
7636 if (!(*i)->is_mark()) {
7637 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7639 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7651 begin_reversible_command (_("insert time"));
7654 XMLNode& after (_session->locations()->get_state());
7655 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7661 begin_reversible_command (_("insert time"));
7664 XMLNode& before (_session->tempo_map().get_state());
7665 _session->tempo_map().insert_time (pos, samples);
7666 XMLNode& after (_session->tempo_map().get_state());
7667 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7671 commit_reversible_command ();
7676 Editor::do_remove_time ()
7678 if (selection->tracks.empty()) {
7682 InsertRemoveTimeDialog d (*this, true);
7684 int response = d.run ();
7686 if (response != RESPONSE_OK) {
7690 samplecnt_t distance = d.distance();
7692 if (distance == 0) {
7702 d.move_glued_markers(),
7703 d.move_locked_markers(),
7709 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7710 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7712 if (Config->get_edit_mode() == Lock) {
7713 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7716 bool in_command = false;
7718 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7720 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7724 XMLNode &before = pl->get_state();
7727 begin_reversible_command (_("remove time"));
7731 std::list<AudioRange> rl;
7732 AudioRange ar(pos, pos+samples, 0);
7735 pl->shift (pos, -samples, true, ignore_music_glue);
7737 XMLNode &after = pl->get_state();
7739 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7743 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7746 begin_reversible_command (_("remove time"));
7749 rtav->route ()->shift (pos, -samples);
7753 const int32_t divisions = get_grid_music_divisions (0);
7754 std::list<Location*> loc_kill_list;
7759 XMLNode& before (_session->locations()->get_state());
7760 Locations::LocationList copy (_session->locations()->list());
7762 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7763 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7765 bool const was_locked = (*i)->locked ();
7766 if (locked_markers_too) {
7770 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7771 if ((*i)->end() >= pos
7772 && (*i)->end() < pos+samples
7773 && (*i)->start() >= pos
7774 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7776 loc_kill_list.push_back(*i);
7777 } else { // only start or end is included, try to do the right thing
7778 // move start before moving end, to avoid trying to move the end to before the start
7779 // if we're removing more time than the length of the range
7780 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7781 // start is within cut
7782 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7784 } else if ((*i)->start() >= pos+samples) {
7785 // start (and thus entire range) lies beyond end of cut
7786 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7789 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7790 // end is inside cut
7791 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7793 } else if ((*i)->end() >= pos+samples) {
7794 // end is beyond end of cut
7795 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7800 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7801 loc_kill_list.push_back(*i);
7803 } else if ((*i)->start() >= pos) {
7804 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7814 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7815 _session->locations()->remove (*i);
7820 begin_reversible_command (_("remove time"));
7823 XMLNode& after (_session->locations()->get_state());
7824 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7829 XMLNode& before (_session->tempo_map().get_state());
7831 if (_session->tempo_map().remove_time (pos, samples)) {
7833 begin_reversible_command (_("remove time"));
7836 XMLNode& after (_session->tempo_map().get_state());
7837 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7842 commit_reversible_command ();
7847 Editor::fit_selection ()
7849 if (!selection->tracks.empty()) {
7850 fit_tracks (selection->tracks);
7854 /* no selected tracks - use tracks with selected regions */
7856 if (!selection->regions.empty()) {
7857 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7858 tvl.push_back (&(*r)->get_time_axis_view ());
7864 } else if (internal_editing()) {
7865 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7868 if (entered_track) {
7869 tvl.push_back (entered_track);
7877 Editor::fit_tracks (TrackViewList & tracks)
7879 if (tracks.empty()) {
7883 uint32_t child_heights = 0;
7884 int visible_tracks = 0;
7886 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7888 if (!(*t)->marked_for_display()) {
7892 child_heights += (*t)->effective_height() - (*t)->current_height();
7896 /* compute the per-track height from:
7898 * total canvas visible height
7899 * - height that will be taken by visible children of selected tracks
7900 * - height of the ruler/hscroll area
7902 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7903 double first_y_pos = DBL_MAX;
7905 if (h < TimeAxisView::preset_height (HeightSmall)) {
7906 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7907 /* too small to be displayed */
7911 undo_visual_stack.push_back (current_visual_state (true));
7912 PBD::Unwinder<bool> nsv (no_save_visual, true);
7914 /* build a list of all tracks, including children */
7917 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7919 TimeAxisView::Children c = (*i)->get_child_list ();
7920 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7921 all.push_back (j->get());
7926 // find selection range.
7927 // if someone knows how to user TrackViewList::iterator for this
7929 int selected_top = -1;
7930 int selected_bottom = -1;
7932 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7933 if ((*t)->marked_for_display ()) {
7934 if (tracks.contains(*t)) {
7935 if (selected_top == -1) {
7938 selected_bottom = i;
7944 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7945 if ((*t)->marked_for_display ()) {
7946 if (tracks.contains(*t)) {
7947 (*t)->set_height (h);
7948 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7950 if (i > selected_top && i < selected_bottom) {
7951 hide_track_in_display (*t);
7958 set the controls_layout height now, because waiting for its size
7959 request signal handler will cause the vertical adjustment setting to fail
7962 controls_layout.property_height () = _full_canvas_height;
7963 vertical_adjustment.set_value (first_y_pos);
7965 redo_visual_stack.push_back (current_visual_state (true));
7967 visible_tracks_selector.set_text (_("Sel"));
7971 Editor::save_visual_state (uint32_t n)
7973 while (visual_states.size() <= n) {
7974 visual_states.push_back (0);
7977 if (visual_states[n] != 0) {
7978 delete visual_states[n];
7981 visual_states[n] = current_visual_state (true);
7986 Editor::goto_visual_state (uint32_t n)
7988 if (visual_states.size() <= n) {
7992 if (visual_states[n] == 0) {
7996 use_visual_state (*visual_states[n]);
8000 Editor::start_visual_state_op (uint32_t n)
8002 save_visual_state (n);
8004 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8006 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8007 pup->set_text (buf);
8012 Editor::cancel_visual_state_op (uint32_t n)
8014 goto_visual_state (n);
8018 Editor::toggle_region_mute ()
8020 if (_ignore_region_action) {
8024 RegionSelection rs = get_regions_from_selection_and_entered ();
8030 if (rs.size() > 1) {
8031 begin_reversible_command (_("mute regions"));
8033 begin_reversible_command (_("mute region"));
8036 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8038 (*i)->region()->playlist()->clear_changes ();
8039 (*i)->region()->set_muted (!(*i)->region()->muted ());
8040 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8044 commit_reversible_command ();
8048 Editor::combine_regions ()
8050 /* foreach track with selected regions, take all selected regions
8051 and join them into a new region containing the subregions (as a
8055 typedef set<RouteTimeAxisView*> RTVS;
8058 if (selection->regions.empty()) {
8062 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8063 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8066 tracks.insert (rtv);
8070 begin_reversible_command (_("combine regions"));
8072 vector<RegionView*> new_selection;
8074 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8077 if ((rv = (*i)->combine_regions ()) != 0) {
8078 new_selection.push_back (rv);
8082 selection->clear_regions ();
8083 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8084 selection->add (*i);
8087 commit_reversible_command ();
8091 Editor::uncombine_regions ()
8093 typedef set<RouteTimeAxisView*> RTVS;
8096 if (selection->regions.empty()) {
8100 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8101 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8104 tracks.insert (rtv);
8108 begin_reversible_command (_("uncombine regions"));
8110 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8111 (*i)->uncombine_regions ();
8114 commit_reversible_command ();
8118 Editor::toggle_midi_input_active (bool flip_others)
8121 boost::shared_ptr<RouteList> rl (new RouteList);
8123 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8124 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8130 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8133 rl->push_back (rtav->route());
8134 onoff = !mt->input_active();
8138 _session->set_exclusive_input_active (rl, onoff, flip_others);
8141 static bool ok_fine (GdkEventAny*) { return true; }
8147 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8149 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8150 lock_dialog->get_vbox()->pack_start (*padlock);
8151 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8153 ArdourButton* b = manage (new ArdourButton);
8154 b->set_name ("lock button");
8155 b->set_text (_("Click to unlock"));
8156 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8157 lock_dialog->get_vbox()->pack_start (*b);
8159 lock_dialog->get_vbox()->show_all ();
8160 lock_dialog->set_size_request (200, 200);
8163 delete _main_menu_disabler;
8164 _main_menu_disabler = new MainMenuDisabler;
8166 lock_dialog->present ();
8168 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8174 lock_dialog->hide ();
8176 delete _main_menu_disabler;
8177 _main_menu_disabler = 0;
8179 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8180 start_lock_event_timing ();
8185 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8187 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8191 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8193 Timers::TimerSuspender t;
8194 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8195 Gtkmm2ext::UI::instance()->flush_pending (1);
8199 Editor::bring_all_sources_into_session ()
8206 ArdourDialog w (_("Moving embedded files into session folder"));
8207 w.get_vbox()->pack_start (msg);
8210 /* flush all pending GUI events because we're about to start copying
8214 Timers::TimerSuspender t;
8215 Gtkmm2ext::UI::instance()->flush_pending (3);
8219 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));