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) {
1983 Editor::temporal_zoom_session ()
1985 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1988 samplecnt_t start = _session->current_start_sample();
1989 samplecnt_t end = _session->current_end_sample();
1991 if (_session->actively_recording () ) {
1992 samplepos_t cur = playhead_cursor->current_sample ();
1994 /* recording beyond the end marker; zoom out
1995 * by 5 seconds more so that if 'follow
1996 * playhead' is active we don't immediately
1999 end = cur + _session->sample_rate() * 5;
2003 if ((start == 0 && end == 0) || end < start) {
2007 calc_extra_zoom_edges(start, end);
2009 temporal_zoom_by_sample (start, end);
2014 Editor::temporal_zoom_extents ()
2016 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
2019 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
2021 samplecnt_t start = ext.first;
2022 samplecnt_t end = ext.second;
2024 if (_session->actively_recording () ) {
2025 samplepos_t cur = playhead_cursor->current_sample ();
2027 /* recording beyond the end marker; zoom out
2028 * by 5 seconds more so that if 'follow
2029 * playhead' is active we don't immediately
2032 end = cur + _session->sample_rate() * 5;
2036 if ((start == 0 && end == 0) || end < start) {
2040 calc_extra_zoom_edges(start, end);
2042 temporal_zoom_by_sample (start, end);
2047 Editor::temporal_zoom_by_sample (samplepos_t start, samplepos_t end)
2049 if (!_session) return;
2051 if ((start == 0 && end == 0) || end < start) {
2055 samplepos_t range = end - start;
2057 const samplecnt_t new_fpp = (samplecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2059 samplepos_t new_page = range;
2060 samplepos_t middle = (samplepos_t) floor ((double) start + ((double) range / 2.0f));
2061 samplepos_t new_leftmost = (samplepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2063 if (new_leftmost > middle) {
2067 if (new_leftmost < 0) {
2071 reposition_and_zoom (new_leftmost, new_fpp);
2075 Editor::temporal_zoom_to_sample (bool coarser, samplepos_t sample)
2081 samplecnt_t range_before = sample - _leftmost_sample;
2082 samplecnt_t new_spp;
2085 if (samples_per_pixel <= 1) {
2088 new_spp = samples_per_pixel + (samples_per_pixel/2);
2090 range_before += range_before/2;
2092 if (samples_per_pixel >= 1) {
2093 new_spp = samples_per_pixel - (samples_per_pixel/2);
2095 /* could bail out here since we cannot zoom any finer,
2096 but leave that to the equality test below
2098 new_spp = samples_per_pixel;
2101 range_before -= range_before/2;
2104 if (new_spp == samples_per_pixel) {
2108 /* zoom focus is automatically taken as @param sample when this
2112 samplepos_t new_leftmost = sample - (samplepos_t)range_before;
2114 if (new_leftmost > sample) {
2118 if (new_leftmost < 0) {
2122 reposition_and_zoom (new_leftmost, new_spp);
2127 Editor::choose_new_marker_name(string &name) {
2129 if (!UIConfiguration::instance().get_name_new_markers()) {
2130 /* don't prompt user for a new name */
2134 Prompter dialog (true);
2136 dialog.set_prompt (_("New Name:"));
2138 dialog.set_title (_("New Location Marker"));
2140 dialog.set_name ("MarkNameWindow");
2141 dialog.set_size_request (250, -1);
2142 dialog.set_position (Gtk::WIN_POS_MOUSE);
2144 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2145 dialog.set_initial_text (name);
2149 switch (dialog.run ()) {
2150 case RESPONSE_ACCEPT:
2156 dialog.get_result(name);
2163 Editor::add_location_from_selection ()
2167 if (selection->time.empty()) {
2171 if (_session == 0 || clicked_axisview == 0) {
2175 samplepos_t start = selection->time[clicked_selection].start;
2176 samplepos_t end = selection->time[clicked_selection].end;
2178 _session->locations()->next_available_name(rangename,"selection");
2179 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2181 begin_reversible_command (_("add marker"));
2183 XMLNode &before = _session->locations()->get_state();
2184 _session->locations()->add (location, true);
2185 XMLNode &after = _session->locations()->get_state();
2186 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2188 commit_reversible_command ();
2192 Editor::add_location_mark (samplepos_t where)
2196 select_new_marker = true;
2198 _session->locations()->next_available_name(markername,"mark");
2199 if (!choose_new_marker_name(markername)) {
2202 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2203 begin_reversible_command (_("add marker"));
2205 XMLNode &before = _session->locations()->get_state();
2206 _session->locations()->add (location, true);
2207 XMLNode &after = _session->locations()->get_state();
2208 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2210 commit_reversible_command ();
2214 Editor::set_session_start_from_playhead ()
2220 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2221 _session->set_session_extents ( _session->audible_sample(), _session->audible_sample() );
2223 XMLNode &before = loc->get_state();
2225 _session->set_session_extents ( _session->audible_sample(), loc->end() );
2227 XMLNode &after = loc->get_state();
2229 begin_reversible_command (_("Set session start"));
2231 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2233 commit_reversible_command ();
2238 Editor::set_session_end_from_playhead ()
2244 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2245 _session->set_session_extents ( _session->audible_sample(), _session->audible_sample() );
2247 XMLNode &before = loc->get_state();
2249 _session->set_session_extents ( loc->start(), _session->audible_sample() );
2251 XMLNode &after = loc->get_state();
2253 begin_reversible_command (_("Set session start"));
2255 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2257 commit_reversible_command ();
2260 _session->set_end_is_free (false);
2265 Editor::toggle_location_at_playhead_cursor ()
2267 if (!do_remove_location_at_playhead_cursor())
2269 add_location_from_playhead_cursor();
2274 Editor::add_location_from_playhead_cursor ()
2276 add_location_mark (_session->audible_sample());
2280 Editor::do_remove_location_at_playhead_cursor ()
2282 bool removed = false;
2285 XMLNode &before = _session->locations()->get_state();
2287 //find location(s) at this time
2288 Locations::LocationList locs;
2289 _session->locations()->find_all_between (_session->audible_sample(), _session->audible_sample()+1, locs, Location::Flags(0));
2290 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2291 if ((*i)->is_mark()) {
2292 _session->locations()->remove (*i);
2299 begin_reversible_command (_("remove marker"));
2300 XMLNode &after = _session->locations()->get_state();
2301 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2302 commit_reversible_command ();
2309 Editor::remove_location_at_playhead_cursor ()
2311 do_remove_location_at_playhead_cursor ();
2314 /** Add a range marker around each selected region */
2316 Editor::add_locations_from_region ()
2318 RegionSelection rs = get_regions_from_selection_and_entered ();
2323 bool commit = false;
2325 XMLNode &before = _session->locations()->get_state();
2327 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2329 boost::shared_ptr<Region> region = (*i)->region ();
2331 Location *location = new Location (*_session, region->position(), region->last_sample(), region->name(), Location::IsRangeMarker, 0);
2333 _session->locations()->add (location, true);
2338 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2339 XMLNode &after = _session->locations()->get_state();
2340 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2341 commit_reversible_command ();
2345 /** Add a single range marker around all selected regions */
2347 Editor::add_location_from_region ()
2349 RegionSelection rs = get_regions_from_selection_and_entered ();
2355 XMLNode &before = _session->locations()->get_state();
2359 if (rs.size() > 1) {
2360 _session->locations()->next_available_name(markername, "regions");
2362 RegionView* rv = *(rs.begin());
2363 boost::shared_ptr<Region> region = rv->region();
2364 markername = region->name();
2367 if (!choose_new_marker_name(markername)) {
2371 // single range spanning all selected
2372 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_sample(), markername, Location::IsRangeMarker, 0);
2373 _session->locations()->add (location, true);
2375 begin_reversible_command (_("add marker"));
2376 XMLNode &after = _session->locations()->get_state();
2377 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2378 commit_reversible_command ();
2384 Editor::jump_forward_to_mark ()
2390 samplepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_sample());
2396 _session->request_locate (pos, _session->transport_rolling());
2400 Editor::jump_backward_to_mark ()
2406 samplepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_sample());
2408 //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...
2409 if ( _session->transport_rolling() ) {
2410 if ( (playhead_cursor->current_sample() - pos) < _session->sample_rate()/2 ) {
2411 samplepos_t prior = _session->locations()->first_mark_before ( pos );
2420 _session->request_locate (pos, _session->transport_rolling());
2426 samplepos_t const pos = _session->audible_sample ();
2429 _session->locations()->next_available_name (markername, "mark");
2431 if (!choose_new_marker_name (markername)) {
2435 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2439 Editor::clear_markers ()
2442 begin_reversible_command (_("clear markers"));
2444 XMLNode &before = _session->locations()->get_state();
2445 _session->locations()->clear_markers ();
2446 XMLNode &after = _session->locations()->get_state();
2447 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2449 commit_reversible_command ();
2454 Editor::clear_ranges ()
2457 begin_reversible_command (_("clear ranges"));
2459 XMLNode &before = _session->locations()->get_state();
2461 _session->locations()->clear_ranges ();
2463 XMLNode &after = _session->locations()->get_state();
2464 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2466 commit_reversible_command ();
2471 Editor::clear_locations ()
2473 begin_reversible_command (_("clear locations"));
2475 XMLNode &before = _session->locations()->get_state();
2476 _session->locations()->clear ();
2477 XMLNode &after = _session->locations()->get_state();
2478 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2480 commit_reversible_command ();
2484 Editor::unhide_markers ()
2486 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2487 Location *l = (*i).first;
2488 if (l->is_hidden() && l->is_mark()) {
2489 l->set_hidden(false, this);
2495 Editor::unhide_ranges ()
2497 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2498 Location *l = (*i).first;
2499 if (l->is_hidden() && l->is_range_marker()) {
2500 l->set_hidden(false, this);
2505 /* INSERT/REPLACE */
2508 Editor::insert_region_list_selection (float times)
2510 RouteTimeAxisView *tv = 0;
2511 boost::shared_ptr<Playlist> playlist;
2513 if (clicked_routeview != 0) {
2514 tv = clicked_routeview;
2515 } else if (!selection->tracks.empty()) {
2516 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2519 } else if (entered_track != 0) {
2520 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2527 if ((playlist = tv->playlist()) == 0) {
2531 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2536 begin_reversible_command (_("insert region"));
2537 playlist->clear_changes ();
2538 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2539 if (Config->get_edit_mode() == Ripple)
2540 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2542 _session->add_command(new StatefulDiffCommand (playlist));
2543 commit_reversible_command ();
2546 /* BUILT-IN EFFECTS */
2549 Editor::reverse_selection ()
2554 /* GAIN ENVELOPE EDITING */
2557 Editor::edit_envelope ()
2564 Editor::transition_to_rolling (bool fwd)
2570 if (_session->config.get_external_sync()) {
2571 switch (Config->get_sync_source()) {
2575 /* transport controlled by the master */
2580 if (_session->is_auditioning()) {
2581 _session->cancel_audition ();
2585 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2589 Editor::play_from_start ()
2591 _session->request_locate (_session->current_start_sample(), true);
2595 Editor::play_from_edit_point ()
2597 _session->request_locate (get_preferred_edit_position(), true);
2601 Editor::play_from_edit_point_and_return ()
2603 samplepos_t start_sample;
2604 samplepos_t return_sample;
2606 start_sample = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2608 if (_session->transport_rolling()) {
2609 _session->request_locate (start_sample, false);
2613 /* don't reset the return sample if its already set */
2615 if ((return_sample = _session->requested_return_sample()) < 0) {
2616 return_sample = _session->audible_sample();
2619 if (start_sample >= 0) {
2620 _session->request_roll_at_and_return (start_sample, return_sample);
2625 Editor::play_selection ()
2627 samplepos_t start, end;
2628 if (!get_selection_extents ( start, end))
2631 AudioRange ar (start, end, 0);
2632 list<AudioRange> lar;
2635 _session->request_play_range (&lar, true);
2640 Editor::maybe_locate_with_edit_preroll (samplepos_t location)
2642 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2645 location -= _session->preroll_samples (location);
2647 //don't try to locate before the beginning of time
2652 //if follow_playhead is on, keep the playhead on the screen
2653 if ( _follow_playhead )
2654 if ( location < _leftmost_sample )
2655 location = _leftmost_sample;
2657 _session->request_locate( location );
2661 Editor::play_with_preroll ()
2663 samplepos_t start, end;
2664 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2665 const samplepos_t preroll = _session->preroll_samples (start);
2667 samplepos_t ret = start;
2669 if (start > preroll) {
2670 start = start - preroll;
2673 end = end + preroll; //"post-roll"
2675 AudioRange ar (start, end, 0);
2676 list<AudioRange> lar;
2679 _session->request_play_range (&lar, true);
2680 _session->set_requested_return_sample (ret); //force auto-return to return to range start, without the preroll
2682 samplepos_t ph = playhead_cursor->current_sample ();
2683 const samplepos_t preroll = _session->preroll_samples (ph);
2686 start = ph - preroll;
2690 _session->request_locate (start, true);
2691 _session->set_requested_return_sample (ph); //force auto-return to return to playhead location, without the preroll
2696 Editor::rec_with_preroll ()
2698 samplepos_t ph = playhead_cursor->current_sample ();
2699 samplepos_t preroll = _session->preroll_samples (ph);
2700 _session->request_preroll_record_trim (ph, preroll);
2704 Editor::rec_with_count_in ()
2706 _session->request_count_in_record ();
2710 Editor::play_location (Location& location)
2712 if (location.start() <= location.end()) {
2716 _session->request_bounded_roll (location.start(), location.end());
2720 Editor::loop_location (Location& location)
2722 if (location.start() <= location.end()) {
2728 if ((tll = transport_loop_location()) != 0) {
2729 tll->set (location.start(), location.end());
2731 // enable looping, reposition and start rolling
2732 _session->request_locate (tll->start(), true);
2733 _session->request_play_loop (true);
2738 Editor::do_layer_operation (LayerOperation op)
2740 if (selection->regions.empty ()) {
2744 bool const multiple = selection->regions.size() > 1;
2748 begin_reversible_command (_("raise regions"));
2750 begin_reversible_command (_("raise region"));
2756 begin_reversible_command (_("raise regions to top"));
2758 begin_reversible_command (_("raise region to top"));
2764 begin_reversible_command (_("lower regions"));
2766 begin_reversible_command (_("lower region"));
2772 begin_reversible_command (_("lower regions to bottom"));
2774 begin_reversible_command (_("lower region"));
2779 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2780 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2781 (*i)->clear_owned_changes ();
2784 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2785 boost::shared_ptr<Region> r = (*i)->region ();
2797 r->lower_to_bottom ();
2801 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2802 vector<Command*> cmds;
2804 _session->add_commands (cmds);
2807 commit_reversible_command ();
2811 Editor::raise_region ()
2813 do_layer_operation (Raise);
2817 Editor::raise_region_to_top ()
2819 do_layer_operation (RaiseToTop);
2823 Editor::lower_region ()
2825 do_layer_operation (Lower);
2829 Editor::lower_region_to_bottom ()
2831 do_layer_operation (LowerToBottom);
2834 /** Show the region editor for the selected regions */
2836 Editor::show_region_properties ()
2838 selection->foreach_regionview (&RegionView::show_region_editor);
2841 /** Show the midi list editor for the selected MIDI regions */
2843 Editor::show_midi_list_editor ()
2845 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2849 Editor::rename_region ()
2851 RegionSelection rs = get_regions_from_selection_and_entered ();
2857 ArdourDialog d (_("Rename Region"), true, false);
2859 Label label (_("New name:"));
2862 hbox.set_spacing (6);
2863 hbox.pack_start (label, false, false);
2864 hbox.pack_start (entry, true, true);
2866 d.get_vbox()->set_border_width (12);
2867 d.get_vbox()->pack_start (hbox, false, false);
2869 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2870 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2872 d.set_size_request (300, -1);
2874 entry.set_text (rs.front()->region()->name());
2875 entry.select_region (0, -1);
2877 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2883 int const ret = d.run();
2887 if (ret != RESPONSE_OK) {
2891 std::string str = entry.get_text();
2892 strip_whitespace_edges (str);
2894 rs.front()->region()->set_name (str);
2895 _regions->redisplay ();
2899 /** Start an audition of the first selected region */
2901 Editor::play_edit_range ()
2903 samplepos_t start, end;
2905 if (get_edit_op_range (start, end)) {
2906 _session->request_bounded_roll (start, end);
2911 Editor::play_selected_region ()
2913 samplepos_t start = max_samplepos;
2914 samplepos_t end = 0;
2916 RegionSelection rs = get_regions_from_selection_and_entered ();
2922 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2923 if ((*i)->region()->position() < start) {
2924 start = (*i)->region()->position();
2926 if ((*i)->region()->last_sample() + 1 > end) {
2927 end = (*i)->region()->last_sample() + 1;
2931 _session->request_bounded_roll (start, end);
2935 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2937 _session->audition_region (region);
2941 Editor::region_from_selection ()
2943 if (clicked_axisview == 0) {
2947 if (selection->time.empty()) {
2951 samplepos_t start = selection->time[clicked_selection].start;
2952 samplepos_t end = selection->time[clicked_selection].end;
2954 TrackViewList tracks = get_tracks_for_range_action ();
2956 samplepos_t selection_cnt = end - start + 1;
2958 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2959 boost::shared_ptr<Region> current;
2960 boost::shared_ptr<Playlist> pl;
2961 samplepos_t internal_start;
2964 if ((pl = (*i)->playlist()) == 0) {
2968 if ((current = pl->top_region_at (start)) == 0) {
2972 internal_start = start - current->position();
2973 RegionFactory::region_name (new_name, current->name(), true);
2977 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2978 plist.add (ARDOUR::Properties::length, selection_cnt);
2979 plist.add (ARDOUR::Properties::name, new_name);
2980 plist.add (ARDOUR::Properties::layer, 0);
2982 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2987 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2989 if (selection->time.empty() || selection->tracks.empty()) {
2993 samplepos_t start, end;
2994 if (clicked_selection) {
2995 start = selection->time[clicked_selection].start;
2996 end = selection->time[clicked_selection].end;
2998 start = selection->time.start();
2999 end = selection->time.end_sample();
3002 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3003 sort_track_selection (ts);
3005 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3006 boost::shared_ptr<Region> current;
3007 boost::shared_ptr<Playlist> playlist;
3008 samplepos_t internal_start;
3011 if ((playlist = (*i)->playlist()) == 0) {
3015 if ((current = playlist->top_region_at(start)) == 0) {
3019 internal_start = start - current->position();
3020 RegionFactory::region_name (new_name, current->name(), true);
3024 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3025 plist.add (ARDOUR::Properties::length, end - start + 1);
3026 plist.add (ARDOUR::Properties::name, new_name);
3028 new_regions.push_back (RegionFactory::create (current, plist));
3033 Editor::split_multichannel_region ()
3035 RegionSelection rs = get_regions_from_selection_and_entered ();
3041 vector< boost::shared_ptr<Region> > v;
3043 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3044 (*x)->region()->separate_by_channel (v);
3049 Editor::new_region_from_selection ()
3051 region_from_selection ();
3052 cancel_selection ();
3056 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3058 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3059 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3060 case Evoral::OverlapNone:
3068 * - selected tracks, or if there are none...
3069 * - tracks containing selected regions, or if there are none...
3074 Editor::get_tracks_for_range_action () const
3078 if (selection->tracks.empty()) {
3080 /* use tracks with selected regions */
3082 RegionSelection rs = selection->regions;
3084 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3085 TimeAxisView* tv = &(*i)->get_time_axis_view();
3087 if (!t.contains (tv)) {
3093 /* no regions and no tracks: use all tracks */
3099 t = selection->tracks;
3102 return t.filter_to_unique_playlists();
3106 Editor::separate_regions_between (const TimeSelection& ts)
3108 bool in_command = false;
3109 boost::shared_ptr<Playlist> playlist;
3110 RegionSelection new_selection;
3112 TrackViewList tmptracks = get_tracks_for_range_action ();
3113 sort_track_selection (tmptracks);
3115 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3117 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3123 if (!rtv->is_track()) {
3127 /* no edits to destructive tracks */
3129 if (rtv->track()->destructive()) {
3133 if ((playlist = rtv->playlist()) != 0) {
3135 playlist->clear_changes ();
3137 /* XXX need to consider musical time selections here at some point */
3139 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3141 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3142 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3144 latest_regionviews.clear ();
3146 playlist->partition ((*t).start, (*t).end, false);
3150 if (!latest_regionviews.empty()) {
3152 rtv->view()->foreach_regionview (sigc::bind (
3153 sigc::ptr_fun (add_if_covered),
3154 &(*t), &new_selection));
3157 begin_reversible_command (_("separate"));
3161 /* pick up changes to existing regions */
3163 vector<Command*> cmds;
3164 playlist->rdiff (cmds);
3165 _session->add_commands (cmds);
3167 /* pick up changes to the playlist itself (adds/removes)
3170 _session->add_command(new StatefulDiffCommand (playlist));
3177 // selection->set (new_selection);
3179 commit_reversible_command ();
3183 struct PlaylistState {
3184 boost::shared_ptr<Playlist> playlist;
3188 /** Take tracks from get_tracks_for_range_action and cut any regions
3189 * on those tracks so that the tracks are empty over the time
3193 Editor::separate_region_from_selection ()
3195 /* preferentially use *all* ranges in the time selection if we're in range mode
3196 to allow discontiguous operation, since get_edit_op_range() currently
3197 returns a single range.
3200 if (!selection->time.empty()) {
3202 separate_regions_between (selection->time);
3209 if (get_edit_op_range (start, end)) {
3211 AudioRange ar (start, end, 1);
3215 separate_regions_between (ts);
3221 Editor::separate_region_from_punch ()
3223 Location* loc = _session->locations()->auto_punch_location();
3225 separate_regions_using_location (*loc);
3230 Editor::separate_region_from_loop ()
3232 Location* loc = _session->locations()->auto_loop_location();
3234 separate_regions_using_location (*loc);
3239 Editor::separate_regions_using_location (Location& loc)
3241 if (loc.is_mark()) {
3245 AudioRange ar (loc.start(), loc.end(), 1);
3250 separate_regions_between (ts);
3253 /** Separate regions under the selected region */
3255 Editor::separate_under_selected_regions ()
3257 vector<PlaylistState> playlists;
3261 rs = get_regions_from_selection_and_entered();
3263 if (!_session || rs.empty()) {
3267 begin_reversible_command (_("separate region under"));
3269 list<boost::shared_ptr<Region> > regions_to_remove;
3271 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3272 // we can't just remove the region(s) in this loop because
3273 // this removes them from the RegionSelection, and they thus
3274 // disappear from underneath the iterator, and the ++i above
3275 // SEGVs in a puzzling fashion.
3277 // so, first iterate over the regions to be removed from rs and
3278 // add them to the regions_to_remove list, and then
3279 // iterate over the list to actually remove them.
3281 regions_to_remove.push_back ((*i)->region());
3284 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3286 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3289 // is this check necessary?
3293 vector<PlaylistState>::iterator i;
3295 //only take state if this is a new playlist.
3296 for (i = playlists.begin(); i != playlists.end(); ++i) {
3297 if ((*i).playlist == playlist) {
3302 if (i == playlists.end()) {
3304 PlaylistState before;
3305 before.playlist = playlist;
3306 before.before = &playlist->get_state();
3307 playlist->clear_changes ();
3308 playlist->freeze ();
3309 playlists.push_back(before);
3312 //Partition on the region bounds
3313 playlist->partition ((*rl)->first_sample() - 1, (*rl)->last_sample() + 1, true);
3315 //Re-add region that was just removed due to the partition operation
3316 playlist->add_region( (*rl), (*rl)->first_sample() );
3319 vector<PlaylistState>::iterator pl;
3321 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3322 (*pl).playlist->thaw ();
3323 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3326 commit_reversible_command ();
3330 Editor::crop_region_to_selection ()
3332 if (!selection->time.empty()) {
3334 begin_reversible_command (_("Crop Regions to Time Selection"));
3335 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3336 crop_region_to ((*i).start, (*i).end);
3338 commit_reversible_command();
3344 if (get_edit_op_range (start, end)) {
3345 begin_reversible_command (_("Crop Regions to Edit Range"));
3347 crop_region_to (start, end);
3349 commit_reversible_command();
3356 Editor::crop_region_to (samplepos_t start, samplepos_t end)
3358 vector<boost::shared_ptr<Playlist> > playlists;
3359 boost::shared_ptr<Playlist> playlist;
3362 if (selection->tracks.empty()) {
3363 ts = track_views.filter_to_unique_playlists();
3365 ts = selection->tracks.filter_to_unique_playlists ();
3368 sort_track_selection (ts);
3370 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3372 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3378 boost::shared_ptr<Track> t = rtv->track();
3380 if (t != 0 && ! t->destructive()) {
3382 if ((playlist = rtv->playlist()) != 0) {
3383 playlists.push_back (playlist);
3388 if (playlists.empty()) {
3393 samplepos_t new_start;
3394 samplepos_t new_end;
3395 samplecnt_t new_length;
3397 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3399 /* Only the top regions at start and end have to be cropped */
3400 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3401 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3403 vector<boost::shared_ptr<Region> > regions;
3405 if (region_at_start != 0) {
3406 regions.push_back (region_at_start);
3408 if (region_at_end != 0) {
3409 regions.push_back (region_at_end);
3412 /* now adjust lengths */
3413 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3415 pos = (*i)->position();
3416 new_start = max (start, pos);
3417 if (max_samplepos - pos > (*i)->length()) {
3418 new_end = pos + (*i)->length() - 1;
3420 new_end = max_samplepos;
3422 new_end = min (end, new_end);
3423 new_length = new_end - new_start + 1;
3425 (*i)->clear_changes ();
3426 (*i)->trim_to (new_start, new_length);
3427 _session->add_command (new StatefulDiffCommand (*i));
3433 Editor::region_fill_track ()
3435 boost::shared_ptr<Playlist> playlist;
3436 RegionSelection regions = get_regions_from_selection_and_entered ();
3437 RegionSelection foo;
3439 samplepos_t const end = _session->current_end_sample ();
3441 if (regions.empty () || regions.end_sample () + 1 >= end) {
3445 samplepos_t const start_sample = regions.start ();
3446 samplepos_t const end_sample = regions.end_sample ();
3447 samplecnt_t const gap = end_sample - start_sample + 1;
3449 begin_reversible_command (Operations::region_fill);
3451 selection->clear_regions ();
3453 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3455 boost::shared_ptr<Region> r ((*i)->region());
3457 TimeAxisView& tv = (*i)->get_time_axis_view();
3458 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3459 latest_regionviews.clear ();
3460 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3462 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
3463 playlist = (*i)->region()->playlist();
3464 playlist->clear_changes ();
3465 playlist->duplicate_until (r, position, gap, end);
3466 _session->add_command(new StatefulDiffCommand (playlist));
3470 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3474 selection->set (foo);
3477 commit_reversible_command ();
3481 Editor::set_region_sync_position ()
3483 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3487 Editor::set_sync_point (samplepos_t where, const RegionSelection& rs)
3489 bool in_command = false;
3491 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3493 if (!(*r)->region()->covers (where)) {
3497 boost::shared_ptr<Region> region ((*r)->region());
3500 begin_reversible_command (_("set sync point"));
3504 region->clear_changes ();
3505 region->set_sync_position (where);
3506 _session->add_command(new StatefulDiffCommand (region));
3510 commit_reversible_command ();
3514 /** Remove the sync positions of the selection */
3516 Editor::remove_region_sync ()
3518 RegionSelection rs = get_regions_from_selection_and_entered ();
3524 begin_reversible_command (_("remove region sync"));
3526 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3528 (*i)->region()->clear_changes ();
3529 (*i)->region()->clear_sync_position ();
3530 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3533 commit_reversible_command ();
3537 Editor::naturalize_region ()
3539 RegionSelection rs = get_regions_from_selection_and_entered ();
3545 if (rs.size() > 1) {
3546 begin_reversible_command (_("move regions to original position"));
3548 begin_reversible_command (_("move region to original position"));
3551 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3552 (*i)->region()->clear_changes ();
3553 (*i)->region()->move_to_natural_position ();
3554 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3557 commit_reversible_command ();
3561 Editor::align_regions (RegionPoint what)
3563 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3569 begin_reversible_command (_("align selection"));
3571 samplepos_t const position = get_preferred_edit_position ();
3573 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3574 align_region_internal ((*i)->region(), what, position);
3577 commit_reversible_command ();
3580 struct RegionSortByTime {
3581 bool operator() (const RegionView* a, const RegionView* b) {
3582 return a->region()->position() < b->region()->position();
3587 Editor::align_regions_relative (RegionPoint point)
3589 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3595 samplepos_t const position = get_preferred_edit_position ();
3597 samplepos_t distance = 0;
3598 samplepos_t pos = 0;
3601 list<RegionView*> sorted;
3602 rs.by_position (sorted);
3604 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3609 if (position > r->position()) {
3610 distance = position - r->position();
3612 distance = r->position() - position;
3618 if (position > r->last_sample()) {
3619 distance = position - r->last_sample();
3620 pos = r->position() + distance;
3622 distance = r->last_sample() - position;
3623 pos = r->position() - distance;
3629 pos = r->adjust_to_sync (position);
3630 if (pos > r->position()) {
3631 distance = pos - r->position();
3633 distance = r->position() - pos;
3639 if (pos == r->position()) {
3643 begin_reversible_command (_("align selection (relative)"));
3645 /* move first one specially */
3647 r->clear_changes ();
3648 r->set_position (pos);
3649 _session->add_command(new StatefulDiffCommand (r));
3651 /* move rest by the same amount */
3655 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3657 boost::shared_ptr<Region> region ((*i)->region());
3659 region->clear_changes ();
3662 region->set_position (region->position() + distance);
3664 region->set_position (region->position() - distance);
3667 _session->add_command(new StatefulDiffCommand (region));
3671 commit_reversible_command ();
3675 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3677 begin_reversible_command (_("align region"));
3678 align_region_internal (region, point, position);
3679 commit_reversible_command ();
3683 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, samplepos_t position)
3685 region->clear_changes ();
3689 region->set_position (region->adjust_to_sync (position));
3693 if (position > region->length()) {
3694 region->set_position (position - region->length());
3699 region->set_position (position);
3703 _session->add_command(new StatefulDiffCommand (region));
3707 Editor::trim_region_front ()
3713 Editor::trim_region_back ()
3715 trim_region (false);
3719 Editor::trim_region (bool front)
3721 samplepos_t where = get_preferred_edit_position();
3722 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3728 begin_reversible_command (front ? _("trim front") : _("trim back"));
3730 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3731 if (!(*i)->region()->locked()) {
3733 (*i)->region()->clear_changes ();
3736 (*i)->region()->trim_front (where);
3738 (*i)->region()->trim_end (where);
3741 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3745 commit_reversible_command ();
3748 /** Trim the end of the selected regions to the position of the edit cursor */
3750 Editor::trim_region_to_loop ()
3752 Location* loc = _session->locations()->auto_loop_location();
3756 trim_region_to_location (*loc, _("trim to loop"));
3760 Editor::trim_region_to_punch ()
3762 Location* loc = _session->locations()->auto_punch_location();
3766 trim_region_to_location (*loc, _("trim to punch"));
3770 Editor::trim_region_to_location (const Location& loc, const char* str)
3772 RegionSelection rs = get_regions_from_selection_and_entered ();
3773 bool in_command = false;
3775 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3776 RegionView* rv = (*x);
3778 /* require region to span proposed trim */
3779 switch (rv->region()->coverage (loc.start(), loc.end())) {
3780 case Evoral::OverlapInternal:
3786 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3794 start = loc.start();
3797 rv->region()->clear_changes ();
3798 rv->region()->trim_to (start, (end - start));
3801 begin_reversible_command (str);
3804 _session->add_command(new StatefulDiffCommand (rv->region()));
3808 commit_reversible_command ();
3813 Editor::trim_region_to_previous_region_end ()
3815 return trim_to_region(false);
3819 Editor::trim_region_to_next_region_start ()
3821 return trim_to_region(true);
3825 Editor::trim_to_region(bool forward)
3827 RegionSelection rs = get_regions_from_selection_and_entered ();
3828 bool in_command = false;
3830 boost::shared_ptr<Region> next_region;
3832 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3834 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3840 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3846 boost::shared_ptr<Region> region = arv->region();
3847 boost::shared_ptr<Playlist> playlist (region->playlist());
3849 region->clear_changes ();
3853 next_region = playlist->find_next_region (region->first_sample(), Start, 1);
3859 region->trim_end (next_region->first_sample() - 1);
3860 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3864 next_region = playlist->find_next_region (region->first_sample(), Start, 0);
3870 region->trim_front (next_region->last_sample() + 1);
3871 arv->region_changed (ARDOUR::bounds_change);
3875 begin_reversible_command (_("trim to region"));
3878 _session->add_command(new StatefulDiffCommand (region));
3882 commit_reversible_command ();
3887 Editor::unfreeze_route ()
3889 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3893 clicked_routeview->track()->unfreeze ();
3897 Editor::_freeze_thread (void* arg)
3899 return static_cast<Editor*>(arg)->freeze_thread ();
3903 Editor::freeze_thread ()
3905 /* create event pool because we may need to talk to the session */
3906 SessionEvent::create_per_thread_pool ("freeze events", 64);
3907 /* create per-thread buffers for process() tree to use */
3908 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3909 current_interthread_info->done = true;
3914 Editor::freeze_route ()
3920 /* stop transport before we start. this is important */
3922 _session->request_transport_speed (0.0);
3924 /* wait for just a little while, because the above call is asynchronous */
3926 Glib::usleep (250000);
3928 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3932 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3934 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3935 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3937 d.set_title (_("Cannot freeze"));
3942 if (clicked_routeview->track()->has_external_redirects()) {
3943 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"
3944 "Freezing will only process the signal as far as the first send/insert/return."),
3945 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3947 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3948 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3949 d.set_title (_("Freeze Limits"));
3951 int response = d.run ();
3954 case Gtk::RESPONSE_CANCEL:
3961 InterThreadInfo itt;
3962 current_interthread_info = &itt;
3964 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3966 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3968 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3970 while (!itt.done && !itt.cancel) {
3971 gtk_main_iteration ();
3974 pthread_join (itt.thread, 0);
3975 current_interthread_info = 0;
3979 Editor::bounce_range_selection (bool replace, bool enable_processing)
3981 if (selection->time.empty()) {
3985 TrackSelection views = selection->tracks;
3987 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3989 if (enable_processing) {
3991 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3993 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3995 _("You can't perform this operation because the processing of the signal "
3996 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3997 "You can do this without processing, which is a different operation.")
3999 d.set_title (_("Cannot bounce"));
4006 samplepos_t start = selection->time[clicked_selection].start;
4007 samplepos_t end = selection->time[clicked_selection].end;
4008 samplepos_t cnt = end - start + 1;
4009 bool in_command = false;
4011 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4013 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4019 boost::shared_ptr<Playlist> playlist;
4021 if ((playlist = rtv->playlist()) == 0) {
4025 InterThreadInfo itt;
4027 playlist->clear_changes ();
4028 playlist->clear_owned_changes ();
4030 boost::shared_ptr<Region> r;
4032 if (enable_processing) {
4033 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4035 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4043 list<AudioRange> ranges;
4044 ranges.push_back (AudioRange (start, start+cnt, 0));
4045 playlist->cut (ranges); // discard result
4046 playlist->add_region (r, start);
4050 begin_reversible_command (_("bounce range"));
4053 vector<Command*> cmds;
4054 playlist->rdiff (cmds);
4055 _session->add_commands (cmds);
4057 _session->add_command (new StatefulDiffCommand (playlist));
4061 commit_reversible_command ();
4065 /** Delete selected regions, automation points or a time range */
4069 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4070 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4071 bool deleted = false;
4072 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4073 deleted = current_mixer_strip->delete_processors ();
4079 /** Cut selected regions, automation points or a time range */
4086 /** Copy selected regions, automation points or a time range */
4094 /** @return true if a Cut, Copy or Clear is possible */
4096 Editor::can_cut_copy () const
4098 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4105 /** Cut, copy or clear selected regions, automation points or a time range.
4106 * @param op Operation (Delete, Cut, Copy or Clear)
4109 Editor::cut_copy (CutCopyOp op)
4111 /* only cancel selection if cut/copy is successful.*/
4117 opname = _("delete");
4126 opname = _("clear");
4130 /* if we're deleting something, and the mouse is still pressed,
4131 the thing we started a drag for will be gone when we release
4132 the mouse button(s). avoid this. see part 2 at the end of
4136 if (op == Delete || op == Cut || op == Clear) {
4137 if (_drags->active ()) {
4142 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4143 cut_buffer->clear ();
4146 if (entered_marker) {
4148 /* cut/delete op while pointing at a marker */
4151 Location* loc = find_location_from_marker (entered_marker, ignored);
4153 if (_session && loc) {
4154 entered_marker = NULL;
4155 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4162 switch (mouse_mode) {
4165 begin_reversible_command (opname + ' ' + X_("MIDI"));
4167 commit_reversible_command ();
4173 bool did_edit = false;
4175 if (!selection->regions.empty() || !selection->points.empty()) {
4176 begin_reversible_command (opname + ' ' + _("objects"));
4179 if (!selection->regions.empty()) {
4180 cut_copy_regions (op, selection->regions);
4182 if (op == Cut || op == Delete) {
4183 selection->clear_regions ();
4187 if (!selection->points.empty()) {
4188 cut_copy_points (op);
4190 if (op == Cut || op == Delete) {
4191 selection->clear_points ();
4194 } else if (selection->time.empty()) {
4195 samplepos_t start, end;
4196 /* no time selection, see if we can get an edit range
4199 if (get_edit_op_range (start, end)) {
4200 selection->set (start, end);
4202 } else if (!selection->time.empty()) {
4203 begin_reversible_command (opname + ' ' + _("range"));
4206 cut_copy_ranges (op);
4208 if (op == Cut || op == Delete) {
4209 selection->clear_time ();
4214 /* reset repeated paste state */
4216 last_paste_pos = -1;
4217 commit_reversible_command ();
4220 if (op == Delete || op == Cut || op == Clear) {
4226 struct AutomationRecord {
4227 AutomationRecord () : state (0) , line(NULL) {}
4228 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4230 XMLNode* state; ///< state before any operation
4231 const AutomationLine* line; ///< line this came from
4232 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4235 struct PointsSelectionPositionSorter {
4236 bool operator() (ControlPoint* a, ControlPoint* b) {
4237 return (*(a->model()))->when < (*(b->model()))->when;
4241 /** Cut, copy or clear selected automation points.
4242 * @param op Operation (Cut, Copy or Clear)
4245 Editor::cut_copy_points (Editing::CutCopyOp op, Temporal::Beats earliest, bool midi)
4247 if (selection->points.empty ()) {
4251 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4252 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4254 /* Keep a record of the AutomationLists that we end up using in this operation */
4255 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4258 /* user could select points in any order */
4259 selection->points.sort(PointsSelectionPositionSorter ());
4261 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4262 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4263 const AutomationLine& line = (*sel_point)->line();
4264 const boost::shared_ptr<AutomationList> al = line.the_list();
4265 if (lists.find (al) == lists.end ()) {
4266 /* We haven't seen this list yet, so make a record for it. This includes
4267 taking a copy of its current state, in case this is needed for undo later.
4269 lists[al] = AutomationRecord (&al->get_state (), &line);
4273 if (op == Cut || op == Copy) {
4274 /* This operation will involve putting things in the cut buffer, so create an empty
4275 ControlList for each of our source lists to put the cut buffer data in.
4277 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4278 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4281 /* Add all selected points to the relevant copy ControlLists */
4282 MusicSample start (std::numeric_limits<samplepos_t>::max(), 0);
4283 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4284 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4285 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4287 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4289 /* Update earliest MIDI start time in beats */
4290 earliest = std::min(earliest, Temporal::Beats((*ctrl_evt)->when));
4292 /* Update earliest session start time in samples */
4293 start.sample = std::min(start.sample, (*sel_point)->line().session_position(ctrl_evt));
4297 /* Snap start time backwards, so copy/paste is snap aligned. */
4299 if (earliest == std::numeric_limits<Temporal::Beats>::max()) {
4300 earliest = Temporal::Beats(); // Weird... don't offset
4302 earliest.round_down_to_beat();
4304 if (start.sample == std::numeric_limits<double>::max()) {
4305 start.sample = 0; // Weird... don't offset
4307 snap_to(start, RoundDownMaybe);
4310 const double line_offset = midi ? earliest.to_double() : start.sample;
4311 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4312 /* Correct this copy list so that it is relative to the earliest
4313 start time, so relative ordering between points is preserved
4314 when copying from several lists and the paste starts at the
4315 earliest copied piece of data. */
4316 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4317 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4318 (*ctrl_evt)->when -= line_offset;
4321 /* And add it to the cut buffer */
4322 cut_buffer->add (al_cpy);
4326 if (op == Delete || op == Cut) {
4327 /* This operation needs to remove things from the main AutomationList, so do that now */
4329 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4330 i->first->freeze ();
4333 /* Remove each selected point from its AutomationList */
4334 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4335 AutomationLine& line = (*sel_point)->line ();
4336 boost::shared_ptr<AutomationList> al = line.the_list();
4340 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4341 /* removing of first and last gain point in region gain lines is prohibited*/
4342 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4348 al->erase ((*sel_point)->model ());
4352 /* Thaw the lists and add undo records for them */
4353 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4354 boost::shared_ptr<AutomationList> al = i->first;
4356 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4361 /** Cut, copy or clear selected automation points.
4362 * @param op Operation (Cut, Copy or Clear)
4365 Editor::cut_copy_midi (CutCopyOp op)
4367 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
4368 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4369 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4371 if (!mrv->selection().empty()) {
4372 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4374 mrv->cut_copy_clear (op);
4376 /* XXX: not ideal, as there may be more than one track involved in the selection */
4377 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4381 if (!selection->points.empty()) {
4382 cut_copy_points (op, earliest, true);
4383 if (op == Cut || op == Delete) {
4384 selection->clear_points ();
4389 struct lt_playlist {
4390 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4391 return a.playlist < b.playlist;
4395 struct PlaylistMapping {
4397 boost::shared_ptr<Playlist> pl;
4399 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4402 /** Remove `clicked_regionview' */
4404 Editor::remove_clicked_region ()
4406 if (clicked_routeview == 0 || clicked_regionview == 0) {
4410 begin_reversible_command (_("remove region"));
4412 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4414 playlist->clear_changes ();
4415 playlist->clear_owned_changes ();
4416 playlist->remove_region (clicked_regionview->region());
4417 if (Config->get_edit_mode() == Ripple)
4418 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4420 /* We might have removed regions, which alters other regions' layering_index,
4421 so we need to do a recursive diff here.
4423 vector<Command*> cmds;
4424 playlist->rdiff (cmds);
4425 _session->add_commands (cmds);
4427 _session->add_command(new StatefulDiffCommand (playlist));
4428 commit_reversible_command ();
4432 /** Remove the selected regions */
4434 Editor::remove_selected_regions ()
4436 RegionSelection rs = get_regions_from_selection_and_entered ();
4438 if (!_session || rs.empty()) {
4442 list<boost::shared_ptr<Region> > regions_to_remove;
4444 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4445 // we can't just remove the region(s) in this loop because
4446 // this removes them from the RegionSelection, and they thus
4447 // disappear from underneath the iterator, and the ++i above
4448 // SEGVs in a puzzling fashion.
4450 // so, first iterate over the regions to be removed from rs and
4451 // add them to the regions_to_remove list, and then
4452 // iterate over the list to actually remove them.
4454 regions_to_remove.push_back ((*i)->region());
4457 vector<boost::shared_ptr<Playlist> > playlists;
4459 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4461 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4464 // is this check necessary?
4468 /* get_regions_from_selection_and_entered() guarantees that
4469 the playlists involved are unique, so there is no need
4473 playlists.push_back (playlist);
4475 playlist->clear_changes ();
4476 playlist->clear_owned_changes ();
4477 playlist->freeze ();
4478 playlist->remove_region (*rl);
4479 if (Config->get_edit_mode() == Ripple)
4480 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4484 vector<boost::shared_ptr<Playlist> >::iterator pl;
4485 bool in_command = false;
4487 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4490 /* We might have removed regions, which alters other regions' layering_index,
4491 so we need to do a recursive diff here.
4495 begin_reversible_command (_("remove region"));
4498 vector<Command*> cmds;
4499 (*pl)->rdiff (cmds);
4500 _session->add_commands (cmds);
4502 _session->add_command(new StatefulDiffCommand (*pl));
4506 commit_reversible_command ();
4510 /** Cut, copy or clear selected regions.
4511 * @param op Operation (Cut, Copy or Clear)
4514 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4516 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4517 a map when we want ordered access to both elements. i think.
4520 vector<PlaylistMapping> pmap;
4522 samplepos_t first_position = max_samplepos;
4524 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4525 FreezeList freezelist;
4527 /* get ordering correct before we cut/copy */
4529 rs.sort_by_position_and_track ();
4531 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4533 first_position = min ((samplepos_t) (*x)->region()->position(), first_position);
4535 if (op == Cut || op == Clear || op == Delete) {
4536 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4539 FreezeList::iterator fl;
4541 // only take state if this is a new playlist.
4542 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4548 if (fl == freezelist.end()) {
4549 pl->clear_changes();
4550 pl->clear_owned_changes ();
4552 freezelist.insert (pl);
4557 TimeAxisView* tv = &(*x)->get_time_axis_view();
4558 vector<PlaylistMapping>::iterator z;
4560 for (z = pmap.begin(); z != pmap.end(); ++z) {
4561 if ((*z).tv == tv) {
4566 if (z == pmap.end()) {
4567 pmap.push_back (PlaylistMapping (tv));
4571 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4573 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4576 /* region not yet associated with a playlist (e.g. unfinished
4583 TimeAxisView& tv = (*x)->get_time_axis_view();
4584 boost::shared_ptr<Playlist> npl;
4585 RegionSelection::iterator tmp;
4592 vector<PlaylistMapping>::iterator z;
4594 for (z = pmap.begin(); z != pmap.end(); ++z) {
4595 if ((*z).tv == &tv) {
4600 assert (z != pmap.end());
4603 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4611 boost::shared_ptr<Region> r = (*x)->region();
4612 boost::shared_ptr<Region> _xx;
4618 pl->remove_region (r);
4619 if (Config->get_edit_mode() == Ripple)
4620 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4624 _xx = RegionFactory::create (r);
4625 npl->add_region (_xx, r->position() - first_position);
4626 pl->remove_region (r);
4627 if (Config->get_edit_mode() == Ripple)
4628 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4632 /* copy region before adding, so we're not putting same object into two different playlists */
4633 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4637 pl->remove_region (r);
4638 if (Config->get_edit_mode() == Ripple)
4639 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4648 list<boost::shared_ptr<Playlist> > foo;
4650 /* the pmap is in the same order as the tracks in which selected regions occurred */
4652 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4655 foo.push_back ((*i).pl);
4660 cut_buffer->set (foo);
4664 _last_cut_copy_source_track = 0;
4666 _last_cut_copy_source_track = pmap.front().tv;
4670 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4673 /* We might have removed regions, which alters other regions' layering_index,
4674 so we need to do a recursive diff here.
4676 vector<Command*> cmds;
4677 (*pl)->rdiff (cmds);
4678 _session->add_commands (cmds);
4680 _session->add_command (new StatefulDiffCommand (*pl));
4685 Editor::cut_copy_ranges (CutCopyOp op)
4687 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4689 /* Sort the track selection now, so that it if is used, the playlists
4690 selected by the calls below to cut_copy_clear are in the order that
4691 their tracks appear in the editor. This makes things like paste
4692 of ranges work properly.
4695 sort_track_selection (ts);
4698 if (!entered_track) {
4701 ts.push_back (entered_track);
4704 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4705 (*i)->cut_copy_clear (*selection, op);
4710 Editor::paste (float times, bool from_context)
4712 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4713 MusicSample where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4714 paste_internal (where.sample, times, 0);
4718 Editor::mouse_paste ()
4720 MusicSample where (0, 0);
4722 if (!mouse_sample (where.sample, ignored)) {
4727 paste_internal (where.sample, 1, where.division);
4731 Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num)
4733 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4735 if (cut_buffer->empty(internal_editing())) {
4739 if (position == max_samplepos) {
4740 position = get_preferred_edit_position();
4741 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4744 if (position == last_paste_pos) {
4745 /* repeated paste in the same position */
4748 /* paste in new location, reset repeated paste state */
4750 last_paste_pos = position;
4753 /* get everything in the correct order */
4756 if (!selection->tracks.empty()) {
4757 /* If there is a track selection, paste into exactly those tracks and
4758 * only those tracks. This allows the user to be explicit and override
4759 * the below "do the reasonable thing" logic. */
4760 ts = selection->tracks.filter_to_unique_playlists ();
4761 sort_track_selection (ts);
4763 /* Figure out which track to base the paste at. */
4764 TimeAxisView* base_track = NULL;
4765 if (_edit_point == Editing::EditAtMouse && entered_track) {
4766 /* With the mouse edit point, paste onto the track under the mouse. */
4767 base_track = entered_track;
4768 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4769 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4770 base_track = &entered_regionview->get_time_axis_view();
4771 } else if (_last_cut_copy_source_track) {
4772 /* Paste to the track that the cut/copy came from (see mantis #333). */
4773 base_track = _last_cut_copy_source_track;
4775 /* This is "impossible" since we've copied... well, do nothing. */
4779 /* Walk up to parent if necessary, so base track is a route. */
4780 while (base_track->get_parent()) {
4781 base_track = base_track->get_parent();
4784 /* Add base track and all tracks below it. The paste logic will select
4785 the appropriate object types from the cut buffer in relative order. */
4786 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4787 if ((*i)->order() >= base_track->order()) {
4792 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4793 sort_track_selection (ts);
4795 /* Add automation children of each track in order, for pasting several lines. */
4796 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4797 /* Add any automation children for pasting several lines */
4798 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4803 typedef RouteTimeAxisView::AutomationTracks ATracks;
4804 const ATracks& atracks = rtv->automation_tracks();
4805 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4806 i = ts.insert(i, a->second.get());
4811 /* We now have a list of trackviews starting at base_track, including
4812 automation children, in the order shown in the editor, e.g. R1,
4813 R1.A1, R1.A2, R2, R2.A1, ... */
4816 begin_reversible_command (Operations::paste);
4818 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4819 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4820 /* Only one line copied, and one automation track selected. Do a
4821 "greedy" paste from one automation type to another. */
4823 PasteContext ctx(paste_count, times, ItemCounts(), true);
4824 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4828 /* Paste into tracks */
4830 PasteContext ctx(paste_count, times, ItemCounts(), false);
4831 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4832 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4836 commit_reversible_command ();
4840 Editor::duplicate_regions (float times)
4842 RegionSelection rs (get_regions_from_selection_and_entered());
4843 duplicate_some_regions (rs, times);
4847 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4849 if (regions.empty ()) {
4853 boost::shared_ptr<Playlist> playlist;
4854 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4855 RegionSelection foo;
4857 samplepos_t const start_sample = regions.start ();
4858 samplepos_t const end_sample = regions.end_sample ();
4859 samplecnt_t const gap = end_sample - start_sample + 1;
4861 begin_reversible_command (Operations::duplicate_region);
4863 selection->clear_regions ();
4865 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4867 boost::shared_ptr<Region> r ((*i)->region());
4869 TimeAxisView& tv = (*i)->get_time_axis_view();
4870 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4871 latest_regionviews.clear ();
4872 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4874 samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
4875 playlist = (*i)->region()->playlist();
4876 playlist->clear_changes ();
4877 playlist->duplicate (r, position, gap, times);
4878 _session->add_command(new StatefulDiffCommand (playlist));
4882 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4886 selection->set (foo);
4889 commit_reversible_command ();
4893 Editor::duplicate_selection (float times)
4895 if (selection->time.empty() || selection->tracks.empty()) {
4899 boost::shared_ptr<Playlist> playlist;
4901 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4903 bool in_command = false;
4905 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4906 if ((playlist = (*i)->playlist()) == 0) {
4909 playlist->clear_changes ();
4911 if (clicked_selection) {
4912 playlist->duplicate_range (selection->time[clicked_selection], times);
4914 playlist->duplicate_ranges (selection->time, times);
4918 begin_reversible_command (_("duplicate range selection"));
4921 _session->add_command (new StatefulDiffCommand (playlist));
4926 if (times == 1.0f) {
4927 // now "move" range selection to after the current range selection
4928 samplecnt_t distance = 0;
4930 if (clicked_selection) {
4932 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4934 distance = selection->time.end_sample () - selection->time.start ();
4937 selection->move_time (distance);
4939 commit_reversible_command ();
4943 /** Reset all selected points to the relevant default value */
4945 Editor::reset_point_selection ()
4947 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4948 ARDOUR::AutomationList::iterator j = (*i)->model ();
4949 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4954 Editor::center_playhead ()
4956 float const page = _visible_canvas_width * samples_per_pixel;
4957 center_screen_internal (playhead_cursor->current_sample (), page);
4961 Editor::center_edit_point ()
4963 float const page = _visible_canvas_width * samples_per_pixel;
4964 center_screen_internal (get_preferred_edit_position(), page);
4967 /** Caller must begin and commit a reversible command */
4969 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4971 playlist->clear_changes ();
4973 _session->add_command (new StatefulDiffCommand (playlist));
4977 Editor::nudge_track (bool use_edit, bool forwards)
4979 boost::shared_ptr<Playlist> playlist;
4980 samplepos_t distance;
4981 samplepos_t next_distance;
4985 start = get_preferred_edit_position();
4990 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4994 if (selection->tracks.empty()) {
4998 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4999 bool in_command = false;
5001 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5003 if ((playlist = (*i)->playlist()) == 0) {
5007 playlist->clear_changes ();
5008 playlist->clear_owned_changes ();
5010 playlist->nudge_after (start, distance, forwards);
5013 begin_reversible_command (_("nudge track"));
5016 vector<Command*> cmds;
5018 playlist->rdiff (cmds);
5019 _session->add_commands (cmds);
5021 _session->add_command (new StatefulDiffCommand (playlist));
5025 commit_reversible_command ();
5030 Editor::remove_last_capture ()
5032 vector<string> choices;
5039 if (Config->get_verify_remove_last_capture()) {
5040 prompt = _("Do you really want to destroy the last capture?"
5041 "\n(This is destructive and cannot be undone)");
5043 choices.push_back (_("No, do nothing."));
5044 choices.push_back (_("Yes, destroy it."));
5046 Choice prompter (_("Destroy last capture"), prompt, choices);
5048 if (prompter.run () == 1) {
5049 _session->remove_last_capture ();
5050 _regions->redisplay ();
5054 _session->remove_last_capture();
5055 _regions->redisplay ();
5060 Editor::normalize_region ()
5066 RegionSelection rs = get_regions_from_selection_and_entered ();
5072 NormalizeDialog dialog (rs.size() > 1);
5074 if (dialog.run () != RESPONSE_ACCEPT) {
5078 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5081 /* XXX: should really only count audio regions here */
5082 int const regions = rs.size ();
5084 /* Make a list of the selected audio regions' maximum amplitudes, and also
5085 obtain the maximum amplitude of them all.
5087 list<double> max_amps;
5088 list<double> rms_vals;
5091 bool use_rms = dialog.constrain_rms ();
5093 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5094 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5098 dialog.descend (1.0 / regions);
5099 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5101 double r = arv->audio_region()->rms (&dialog);
5102 max_rms = max (max_rms, r);
5103 rms_vals.push_back (r);
5107 /* the user cancelled the operation */
5111 max_amps.push_back (a);
5112 max_amp = max (max_amp, a);
5116 list<double>::const_iterator a = max_amps.begin ();
5117 list<double>::const_iterator l = rms_vals.begin ();
5118 bool in_command = false;
5120 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5121 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5126 arv->region()->clear_changes ();
5128 double amp = dialog.normalize_individually() ? *a : max_amp;
5129 double target = dialog.target_peak (); // dB
5132 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5133 const double t_rms = dialog.target_rms ();
5134 const gain_t c_peak = dB_to_coefficient (target);
5135 const gain_t c_rms = dB_to_coefficient (t_rms);
5136 if ((amp_rms / c_rms) > (amp / c_peak)) {
5142 arv->audio_region()->normalize (amp, target);
5145 begin_reversible_command (_("normalize"));
5148 _session->add_command (new StatefulDiffCommand (arv->region()));
5155 commit_reversible_command ();
5161 Editor::reset_region_scale_amplitude ()
5167 RegionSelection rs = get_regions_from_selection_and_entered ();
5173 bool in_command = false;
5175 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5176 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5179 arv->region()->clear_changes ();
5180 arv->audio_region()->set_scale_amplitude (1.0f);
5183 begin_reversible_command ("reset gain");
5186 _session->add_command (new StatefulDiffCommand (arv->region()));
5190 commit_reversible_command ();
5195 Editor::adjust_region_gain (bool up)
5197 RegionSelection rs = get_regions_from_selection_and_entered ();
5199 if (!_session || rs.empty()) {
5203 bool in_command = false;
5205 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5206 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5211 arv->region()->clear_changes ();
5213 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5221 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5224 begin_reversible_command ("adjust region gain");
5227 _session->add_command (new StatefulDiffCommand (arv->region()));
5231 commit_reversible_command ();
5236 Editor::reset_region_gain ()
5238 RegionSelection rs = get_regions_from_selection_and_entered ();
5240 if (!_session || rs.empty()) {
5244 bool in_command = false;
5246 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5247 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5252 arv->region()->clear_changes ();
5254 arv->audio_region()->set_scale_amplitude (1.0f);
5257 begin_reversible_command ("reset region gain");
5260 _session->add_command (new StatefulDiffCommand (arv->region()));
5264 commit_reversible_command ();
5269 Editor::reverse_region ()
5275 Reverse rev (*_session);
5276 apply_filter (rev, _("reverse regions"));
5280 Editor::strip_region_silence ()
5286 RegionSelection rs = get_regions_from_selection_and_entered ();
5292 std::list<RegionView*> audio_only;
5294 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5295 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5297 audio_only.push_back (arv);
5301 assert (!audio_only.empty());
5303 StripSilenceDialog d (_session, audio_only);
5304 int const r = d.run ();
5308 if (r == Gtk::RESPONSE_OK) {
5309 ARDOUR::AudioIntervalMap silences;
5310 d.silences (silences);
5311 StripSilence s (*_session, silences, d.fade_length());
5313 apply_filter (s, _("strip silence"), &d);
5318 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5320 Evoral::Sequence<Temporal::Beats>::Notes selected;
5321 mrv.selection_as_notelist (selected, true);
5323 vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
5324 v.push_back (selected);
5326 Temporal::Beats pos_beats = Temporal::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5328 return op (mrv.midi_region()->model(), pos_beats, v);
5332 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5338 bool in_command = false;
5340 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5341 RegionSelection::const_iterator tmp = r;
5344 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5347 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5350 begin_reversible_command (op.name ());
5354 _session->add_command (cmd);
5362 commit_reversible_command ();
5363 _session->set_dirty ();
5368 Editor::fork_region ()
5370 RegionSelection rs = get_regions_from_selection_and_entered ();
5376 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5377 bool in_command = false;
5381 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5382 RegionSelection::iterator tmp = r;
5385 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5389 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5390 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5391 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5394 begin_reversible_command (_("Fork Region(s)"));
5397 playlist->clear_changes ();
5398 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5399 _session->add_command(new StatefulDiffCommand (playlist));
5401 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5409 commit_reversible_command ();
5414 Editor::quantize_region ()
5417 quantize_regions(get_regions_from_selection_and_entered ());
5422 Editor::quantize_regions (const RegionSelection& rs)
5424 if (rs.n_midi_regions() == 0) {
5428 if (!quantize_dialog) {
5429 quantize_dialog = new QuantizeDialog (*this);
5432 if (quantize_dialog->is_mapped()) {
5433 /* in progress already */
5437 quantize_dialog->present ();
5438 const int r = quantize_dialog->run ();
5439 quantize_dialog->hide ();
5441 if (r == Gtk::RESPONSE_OK) {
5442 Quantize quant (quantize_dialog->snap_start(),
5443 quantize_dialog->snap_end(),
5444 quantize_dialog->start_grid_size(),
5445 quantize_dialog->end_grid_size(),
5446 quantize_dialog->strength(),
5447 quantize_dialog->swing(),
5448 quantize_dialog->threshold());
5450 apply_midi_note_edit_op (quant, rs);
5455 Editor::legatize_region (bool shrink_only)
5458 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5463 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5465 if (rs.n_midi_regions() == 0) {
5469 Legatize legatize(shrink_only);
5470 apply_midi_note_edit_op (legatize, rs);
5474 Editor::transform_region ()
5477 transform_regions(get_regions_from_selection_and_entered ());
5482 Editor::transform_regions (const RegionSelection& rs)
5484 if (rs.n_midi_regions() == 0) {
5491 const int r = td.run();
5494 if (r == Gtk::RESPONSE_OK) {
5495 Transform transform(td.get());
5496 apply_midi_note_edit_op(transform, rs);
5501 Editor::transpose_region ()
5504 transpose_regions(get_regions_from_selection_and_entered ());
5509 Editor::transpose_regions (const RegionSelection& rs)
5511 if (rs.n_midi_regions() == 0) {
5516 int const r = d.run ();
5518 if (r == RESPONSE_ACCEPT) {
5519 Transpose transpose(d.semitones ());
5520 apply_midi_note_edit_op (transpose, rs);
5525 Editor::insert_patch_change (bool from_context)
5527 RegionSelection rs = get_regions_from_selection_and_entered ();
5533 const samplepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5535 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5536 there may be more than one, but the PatchChangeDialog can only offer
5537 one set of patch menus.
5539 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5541 Evoral::PatchChange<Temporal::Beats> empty (Temporal::Beats(), 0, 0, 0);
5542 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5544 if (d.run() == RESPONSE_CANCEL) {
5548 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5549 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5551 if (p >= mrv->region()->first_sample() && p <= mrv->region()->last_sample()) {
5552 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5559 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5561 RegionSelection rs = get_regions_from_selection_and_entered ();
5567 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5568 bool in_command = false;
5573 int const N = rs.size ();
5575 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5576 RegionSelection::iterator tmp = r;
5579 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5581 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5584 progress->descend (1.0 / N);
5587 if (arv->audio_region()->apply (filter, progress) == 0) {
5589 playlist->clear_changes ();
5590 playlist->clear_owned_changes ();
5593 begin_reversible_command (command);
5597 if (filter.results.empty ()) {
5599 /* no regions returned; remove the old one */
5600 playlist->remove_region (arv->region ());
5604 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5606 /* first region replaces the old one */
5607 playlist->replace_region (arv->region(), *res, (*res)->position());
5611 while (res != filter.results.end()) {
5612 playlist->add_region (*res, (*res)->position());
5618 /* We might have removed regions, which alters other regions' layering_index,
5619 so we need to do a recursive diff here.
5621 vector<Command*> cmds;
5622 playlist->rdiff (cmds);
5623 _session->add_commands (cmds);
5625 _session->add_command(new StatefulDiffCommand (playlist));
5629 progress->ascend ();
5638 commit_reversible_command ();
5643 Editor::external_edit_region ()
5649 Editor::reset_region_gain_envelopes ()
5651 RegionSelection rs = get_regions_from_selection_and_entered ();
5653 if (!_session || rs.empty()) {
5657 bool in_command = false;
5659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5660 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5662 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5663 XMLNode& before (alist->get_state());
5665 arv->audio_region()->set_default_envelope ();
5668 begin_reversible_command (_("reset region gain"));
5671 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5676 commit_reversible_command ();
5681 Editor::set_region_gain_visibility (RegionView* rv)
5683 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5685 arv->update_envelope_visibility();
5690 Editor::set_gain_envelope_visibility ()
5696 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5697 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5699 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5705 Editor::toggle_gain_envelope_active ()
5707 if (_ignore_region_action) {
5711 RegionSelection rs = get_regions_from_selection_and_entered ();
5713 if (!_session || rs.empty()) {
5717 bool in_command = false;
5719 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5720 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5722 arv->region()->clear_changes ();
5723 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5726 begin_reversible_command (_("region gain envelope active"));
5729 _session->add_command (new StatefulDiffCommand (arv->region()));
5734 commit_reversible_command ();
5739 Editor::toggle_region_lock ()
5741 if (_ignore_region_action) {
5745 RegionSelection rs = get_regions_from_selection_and_entered ();
5747 if (!_session || rs.empty()) {
5751 begin_reversible_command (_("toggle region lock"));
5753 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5754 (*i)->region()->clear_changes ();
5755 (*i)->region()->set_locked (!(*i)->region()->locked());
5756 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5759 commit_reversible_command ();
5763 Editor::toggle_region_video_lock ()
5765 if (_ignore_region_action) {
5769 RegionSelection rs = get_regions_from_selection_and_entered ();
5771 if (!_session || rs.empty()) {
5775 begin_reversible_command (_("Toggle Video Lock"));
5777 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5778 (*i)->region()->clear_changes ();
5779 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5780 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5783 commit_reversible_command ();
5787 Editor::toggle_region_lock_style ()
5789 if (_ignore_region_action) {
5793 RegionSelection rs = get_regions_from_selection_and_entered ();
5795 if (!_session || rs.empty()) {
5799 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5800 vector<Widget*> proxies = a->get_proxies();
5801 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5805 begin_reversible_command (_("toggle region lock style"));
5807 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5808 (*i)->region()->clear_changes ();
5809 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5810 (*i)->region()->set_position_lock_style (ns);
5811 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5814 commit_reversible_command ();
5818 Editor::toggle_opaque_region ()
5820 if (_ignore_region_action) {
5824 RegionSelection rs = get_regions_from_selection_and_entered ();
5826 if (!_session || rs.empty()) {
5830 begin_reversible_command (_("change region opacity"));
5832 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5833 (*i)->region()->clear_changes ();
5834 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5835 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5838 commit_reversible_command ();
5842 Editor::toggle_record_enable ()
5844 bool new_state = false;
5846 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5847 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5850 if (!rtav->is_track())
5854 new_state = !rtav->track()->rec_enable_control()->get_value();
5858 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5863 Editor::toggle_solo ()
5865 bool new_state = false;
5867 boost::shared_ptr<ControlList> cl (new ControlList);
5869 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5870 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5872 if (!stav || !stav->stripable()->solo_control()) {
5877 new_state = !stav->stripable()->solo_control()->soloed ();
5881 cl->push_back (stav->stripable()->solo_control());
5884 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5888 Editor::toggle_mute ()
5890 bool new_state = false;
5892 boost::shared_ptr<ControlList> cl (new ControlList);
5894 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5895 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5897 if (!stav || !stav->stripable()->mute_control()) {
5902 new_state = !stav->stripable()->mute_control()->muted();
5906 cl->push_back (stav->stripable()->mute_control());
5909 _session->set_controls (cl, new_state, Controllable::UseGroup);
5913 Editor::toggle_solo_isolate ()
5919 Editor::fade_range ()
5921 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5923 begin_reversible_command (_("fade range"));
5925 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5926 (*i)->fade_range (selection->time);
5929 commit_reversible_command ();
5934 Editor::set_fade_length (bool in)
5936 RegionSelection rs = get_regions_from_selection_and_entered ();
5942 /* we need a region to measure the offset from the start */
5944 RegionView* rv = rs.front ();
5946 samplepos_t pos = get_preferred_edit_position();
5950 if (pos > rv->region()->last_sample() || pos < rv->region()->first_sample()) {
5951 /* edit point is outside the relevant region */
5956 if (pos <= rv->region()->position()) {
5960 len = pos - rv->region()->position();
5961 cmd = _("set fade in length");
5963 if (pos >= rv->region()->last_sample()) {
5967 len = rv->region()->last_sample() - pos;
5968 cmd = _("set fade out length");
5971 bool in_command = false;
5973 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5974 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5980 boost::shared_ptr<AutomationList> alist;
5982 alist = tmp->audio_region()->fade_in();
5984 alist = tmp->audio_region()->fade_out();
5987 XMLNode &before = alist->get_state();
5990 tmp->audio_region()->set_fade_in_length (len);
5991 tmp->audio_region()->set_fade_in_active (true);
5993 tmp->audio_region()->set_fade_out_length (len);
5994 tmp->audio_region()->set_fade_out_active (true);
5998 begin_reversible_command (cmd);
6001 XMLNode &after = alist->get_state();
6002 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6006 commit_reversible_command ();
6011 Editor::set_fade_in_shape (FadeShape shape)
6013 RegionSelection rs = get_regions_from_selection_and_entered ();
6018 bool in_command = false;
6020 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6021 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6027 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6028 XMLNode &before = alist->get_state();
6030 tmp->audio_region()->set_fade_in_shape (shape);
6033 begin_reversible_command (_("set fade in shape"));
6036 XMLNode &after = alist->get_state();
6037 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6041 commit_reversible_command ();
6046 Editor::set_fade_out_shape (FadeShape shape)
6048 RegionSelection rs = get_regions_from_selection_and_entered ();
6053 bool in_command = false;
6055 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6056 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6062 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6063 XMLNode &before = alist->get_state();
6065 tmp->audio_region()->set_fade_out_shape (shape);
6068 begin_reversible_command (_("set fade out shape"));
6071 XMLNode &after = alist->get_state();
6072 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6076 commit_reversible_command ();
6081 Editor::set_fade_in_active (bool yn)
6083 RegionSelection rs = get_regions_from_selection_and_entered ();
6088 bool in_command = false;
6090 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6091 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6098 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6100 ar->clear_changes ();
6101 ar->set_fade_in_active (yn);
6104 begin_reversible_command (_("set fade in active"));
6107 _session->add_command (new StatefulDiffCommand (ar));
6111 commit_reversible_command ();
6116 Editor::set_fade_out_active (bool yn)
6118 RegionSelection rs = get_regions_from_selection_and_entered ();
6123 bool in_command = false;
6125 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6126 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6132 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6134 ar->clear_changes ();
6135 ar->set_fade_out_active (yn);
6138 begin_reversible_command (_("set fade out active"));
6141 _session->add_command(new StatefulDiffCommand (ar));
6145 commit_reversible_command ();
6150 Editor::toggle_region_fades (int dir)
6152 if (_ignore_region_action) {
6156 boost::shared_ptr<AudioRegion> ar;
6159 RegionSelection rs = get_regions_from_selection_and_entered ();
6165 RegionSelection::iterator i;
6166 for (i = rs.begin(); i != rs.end(); ++i) {
6167 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6169 yn = ar->fade_out_active ();
6171 yn = ar->fade_in_active ();
6177 if (i == rs.end()) {
6181 /* XXX should this undo-able? */
6182 bool in_command = false;
6184 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6185 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6188 ar->clear_changes ();
6190 if (dir == 1 || dir == 0) {
6191 ar->set_fade_in_active (!yn);
6194 if (dir == -1 || dir == 0) {
6195 ar->set_fade_out_active (!yn);
6198 begin_reversible_command (_("toggle fade active"));
6201 _session->add_command(new StatefulDiffCommand (ar));
6205 commit_reversible_command ();
6210 /** Update region fade visibility after its configuration has been changed */
6212 Editor::update_region_fade_visibility ()
6214 bool _fade_visibility = _session->config.get_show_region_fades ();
6216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6217 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6219 if (_fade_visibility) {
6220 v->audio_view()->show_all_fades ();
6222 v->audio_view()->hide_all_fades ();
6229 Editor::set_edit_point ()
6232 MusicSample where (0, 0);
6234 if (!mouse_sample (where.sample, ignored)) {
6240 if (selection->markers.empty()) {
6242 mouse_add_new_marker (where.sample);
6247 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6250 loc->move_to (where.sample, where.division);
6256 Editor::set_playhead_cursor ()
6258 if (entered_marker) {
6259 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6261 MusicSample where (0, 0);
6264 if (!mouse_sample (where.sample, ignored)) {
6271 _session->request_locate (where.sample, _session->transport_rolling());
6275 //not sure what this was for; remove it for now.
6276 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6277 // cancel_time_selection();
6283 Editor::split_region ()
6285 if (_drags->active ()) {
6289 //if a range is selected, separate it
6290 if ( !selection->time.empty()) {
6291 separate_regions_between (selection->time);
6295 //if no range was selected, try to find some regions to split
6296 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6298 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6299 const samplepos_t pos = get_preferred_edit_position();
6300 const int32_t division = get_grid_music_divisions (0);
6301 MusicSample where (pos, division);
6307 split_regions_at (where, rs);
6313 Editor::select_next_stripable (bool routes_only)
6315 if (selection->tracks.empty()) {
6316 selection->set (track_views.front());
6320 TimeAxisView* current = selection->tracks.front();
6324 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6326 if (*i == current) {
6328 if (i != track_views.end()) {
6331 current = (*(track_views.begin()));
6332 //selection->set (*(track_views.begin()));
6339 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6340 valid = rui && rui->route()->active();
6342 valid = 0 != current->stripable ().get();
6345 } while (current->hidden() || !valid);
6347 selection->set (current);
6349 ensure_time_axis_view_is_visible (*current, false);
6353 Editor::select_prev_stripable (bool routes_only)
6355 if (selection->tracks.empty()) {
6356 selection->set (track_views.front());
6360 TimeAxisView* current = selection->tracks.front();
6364 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6366 if (*i == current) {
6368 if (i != track_views.rend()) {
6371 current = *(track_views.rbegin());
6377 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6378 valid = rui && rui->route()->active();
6380 valid = 0 != current->stripable ().get();
6383 } while (current->hidden() || !valid);
6385 selection->set (current);
6387 ensure_time_axis_view_is_visible (*current, false);
6391 Editor::set_loop_from_selection (bool play)
6393 if (_session == 0) {
6397 samplepos_t start, end;
6398 if (!get_selection_extents ( start, end))
6401 set_loop_range (start, end, _("set loop range from selection"));
6404 _session->request_play_loop (true, true);
6409 Editor::set_loop_from_region (bool play)
6411 samplepos_t start, end;
6412 if (!get_selection_extents ( start, end))
6415 set_loop_range (start, end, _("set loop range from region"));
6418 _session->request_locate (start, true);
6419 _session->request_play_loop (true);
6424 Editor::set_punch_from_selection ()
6426 if (_session == 0) {
6430 samplepos_t start, end;
6431 if (!get_selection_extents ( start, end))
6434 set_punch_range (start, end, _("set punch range from selection"));
6438 Editor::set_auto_punch_range ()
6440 // auto punch in/out button from a single button
6441 // If Punch In is unset, set punch range from playhead to end, enable punch in
6442 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6443 // rewound beyond the Punch In marker, in which case that marker will be moved back
6444 // to the current playhead position.
6445 // If punch out is set, it clears the punch range and Punch In/Out buttons
6447 if (_session == 0) {
6451 Location* tpl = transport_punch_location();
6452 samplepos_t now = playhead_cursor->current_sample();
6453 samplepos_t begin = now;
6454 samplepos_t end = _session->current_end_sample();
6456 if (!_session->config.get_punch_in()) {
6457 // First Press - set punch in and create range from here to eternity
6458 set_punch_range (begin, end, _("Auto Punch In"));
6459 _session->config.set_punch_in(true);
6460 } else if (tpl && !_session->config.get_punch_out()) {
6461 // Second press - update end range marker and set punch_out
6462 if (now < tpl->start()) {
6463 // playhead has been rewound - move start back and pretend nothing happened
6465 set_punch_range (begin, end, _("Auto Punch In/Out"));
6467 // normal case for 2nd press - set the punch out
6468 end = playhead_cursor->current_sample ();
6469 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6470 _session->config.set_punch_out(true);
6473 if (_session->config.get_punch_out()) {
6474 _session->config.set_punch_out(false);
6477 if (_session->config.get_punch_in()) {
6478 _session->config.set_punch_in(false);
6483 // third press - unset punch in/out and remove range
6484 _session->locations()->remove(tpl);
6491 Editor::set_session_extents_from_selection ()
6493 if (_session == 0) {
6497 samplepos_t start, end;
6498 if (!get_selection_extents ( start, end))
6502 if ((loc = _session->locations()->session_range_location()) == 0) {
6503 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6505 XMLNode &before = loc->get_state();
6507 _session->set_session_extents (start, end);
6509 XMLNode &after = loc->get_state();
6511 begin_reversible_command (_("set session start/end from selection"));
6513 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6515 commit_reversible_command ();
6518 _session->set_end_is_free (false);
6522 Editor::set_punch_start_from_edit_point ()
6526 MusicSample start (0, 0);
6527 samplepos_t end = max_samplepos;
6529 //use the existing punch end, if any
6530 Location* tpl = transport_punch_location();
6535 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6536 start.sample = _session->audible_sample();
6538 start.sample = get_preferred_edit_position();
6541 //snap the selection start/end
6544 //if there's not already a sensible selection endpoint, go "forever"
6545 if (start.sample > end ) {
6546 end = max_samplepos;
6549 set_punch_range (start.sample, end, _("set punch start from EP"));
6555 Editor::set_punch_end_from_edit_point ()
6559 samplepos_t start = 0;
6560 MusicSample end (max_samplepos, 0);
6562 //use the existing punch start, if any
6563 Location* tpl = transport_punch_location();
6565 start = tpl->start();
6568 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6569 end.sample = _session->audible_sample();
6571 end.sample = get_preferred_edit_position();
6574 //snap the selection start/end
6577 set_punch_range (start, end.sample, _("set punch end from EP"));
6583 Editor::set_loop_start_from_edit_point ()
6587 MusicSample start (0, 0);
6588 samplepos_t end = max_samplepos;
6590 //use the existing loop end, if any
6591 Location* tpl = transport_loop_location();
6596 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6597 start.sample = _session->audible_sample();
6599 start.sample = get_preferred_edit_position();
6602 //snap the selection start/end
6605 //if there's not already a sensible selection endpoint, go "forever"
6606 if (start.sample > end ) {
6607 end = max_samplepos;
6610 set_loop_range (start.sample, end, _("set loop start from EP"));
6616 Editor::set_loop_end_from_edit_point ()
6620 samplepos_t start = 0;
6621 MusicSample end (max_samplepos, 0);
6623 //use the existing loop start, if any
6624 Location* tpl = transport_loop_location();
6626 start = tpl->start();
6629 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6630 end.sample = _session->audible_sample();
6632 end.sample = get_preferred_edit_position();
6635 //snap the selection start/end
6638 set_loop_range (start, end.sample, _("set loop end from EP"));
6643 Editor::set_punch_from_region ()
6645 samplepos_t start, end;
6646 if (!get_selection_extents ( start, end))
6649 set_punch_range (start, end, _("set punch range from region"));
6653 Editor::pitch_shift_region ()
6655 RegionSelection rs = get_regions_from_selection_and_entered ();
6657 RegionSelection audio_rs;
6658 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6659 if (dynamic_cast<AudioRegionView*> (*i)) {
6660 audio_rs.push_back (*i);
6664 if (audio_rs.empty()) {
6668 pitch_shift (audio_rs, 1.2);
6672 Editor::set_tempo_from_region ()
6674 RegionSelection rs = get_regions_from_selection_and_entered ();
6676 if (!_session || rs.empty()) {
6680 RegionView* rv = rs.front();
6682 define_one_bar (rv->region()->position(), rv->region()->last_sample() + 1);
6686 Editor::use_range_as_bar ()
6688 samplepos_t start, end;
6689 if (get_edit_op_range (start, end)) {
6690 define_one_bar (start, end);
6695 Editor::define_one_bar (samplepos_t start, samplepos_t end)
6697 samplepos_t length = end - start;
6699 const Meter& m (_session->tempo_map().meter_at_sample (start));
6701 /* length = 1 bar */
6703 /* We're going to deliver a constant tempo here,
6704 so we can use samples per beat to determine length.
6705 now we want samples per beat.
6706 we have samples per bar, and beats per bar, so ...
6709 /* XXXX METER MATH */
6711 double samples_per_beat = length / m.divisions_per_bar();
6713 /* beats per minute = */
6715 double beats_per_minute = (_session->sample_rate() * 60.0) / samples_per_beat;
6717 /* now decide whether to:
6719 (a) set global tempo
6720 (b) add a new tempo marker
6724 const TempoSection& t (_session->tempo_map().tempo_section_at_sample (start));
6726 bool do_global = false;
6728 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6730 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6731 at the start, or create a new marker
6734 vector<string> options;
6735 options.push_back (_("Cancel"));
6736 options.push_back (_("Add new marker"));
6737 options.push_back (_("Set global tempo"));
6740 _("Define one bar"),
6741 _("Do you want to set the global tempo or add a new tempo marker?"),
6745 c.set_default_response (2);
6761 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6762 if the marker is at the region starter, change it, otherwise add
6767 begin_reversible_command (_("set tempo from region"));
6768 XMLNode& before (_session->tempo_map().get_state());
6771 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6772 } else if (t.sample() == start) {
6773 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6775 /* constant tempo */
6776 const Tempo tempo (beats_per_minute, t.note_type());
6777 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6780 XMLNode& after (_session->tempo_map().get_state());
6782 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6783 commit_reversible_command ();
6787 Editor::split_region_at_transients ()
6789 AnalysisFeatureList positions;
6791 RegionSelection rs = get_regions_from_selection_and_entered ();
6793 if (!_session || rs.empty()) {
6797 begin_reversible_command (_("split regions"));
6799 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6801 RegionSelection::iterator tmp;
6806 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6809 ar->transients (positions);
6810 split_region_at_points ((*i)->region(), positions, true);
6817 commit_reversible_command ();
6822 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6824 bool use_rhythmic_rodent = false;
6826 boost::shared_ptr<Playlist> pl = r->playlist();
6828 list<boost::shared_ptr<Region> > new_regions;
6834 if (positions.empty()) {
6838 if (positions.size() > 20 && can_ferret) {
6839 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);
6840 MessageDialog msg (msgstr,
6843 Gtk::BUTTONS_OK_CANCEL);
6846 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6847 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6849 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6852 msg.set_title (_("Excessive split?"));
6855 int response = msg.run();
6861 case RESPONSE_APPLY:
6862 use_rhythmic_rodent = true;
6869 if (use_rhythmic_rodent) {
6870 show_rhythm_ferret ();
6874 AnalysisFeatureList::const_iterator x;
6876 pl->clear_changes ();
6877 pl->clear_owned_changes ();
6879 x = positions.begin();
6881 if (x == positions.end()) {
6886 pl->remove_region (r);
6888 samplepos_t pos = 0;
6890 samplepos_t rstart = r->first_sample ();
6891 samplepos_t rend = r->last_sample ();
6893 while (x != positions.end()) {
6895 /* deal with positons that are out of scope of present region bounds */
6896 if (*x <= rstart || *x > rend) {
6901 /* file start = original start + how far we from the initial position ? */
6903 samplepos_t file_start = r->start() + pos;
6905 /* length = next position - current position */
6907 samplepos_t len = (*x) - pos - rstart;
6909 /* XXX we do we really want to allow even single-sample regions?
6910 * shouldn't we have some kind of lower limit on region size?
6919 if (RegionFactory::region_name (new_name, r->name())) {
6923 /* do NOT announce new regions 1 by one, just wait till they are all done */
6927 plist.add (ARDOUR::Properties::start, file_start);
6928 plist.add (ARDOUR::Properties::length, len);
6929 plist.add (ARDOUR::Properties::name, new_name);
6930 plist.add (ARDOUR::Properties::layer, 0);
6931 // TODO set transients_offset
6933 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6934 /* because we set annouce to false, manually add the new region to the
6937 RegionFactory::map_add (nr);
6939 pl->add_region (nr, rstart + pos);
6942 new_regions.push_front(nr);
6951 RegionFactory::region_name (new_name, r->name());
6953 /* Add the final region */
6956 plist.add (ARDOUR::Properties::start, r->start() + pos);
6957 plist.add (ARDOUR::Properties::length, r->last_sample() - (r->position() + pos) + 1);
6958 plist.add (ARDOUR::Properties::name, new_name);
6959 plist.add (ARDOUR::Properties::layer, 0);
6961 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6962 /* because we set annouce to false, manually add the new region to the
6965 RegionFactory::map_add (nr);
6966 pl->add_region (nr, r->position() + pos);
6969 new_regions.push_front(nr);
6974 /* We might have removed regions, which alters other regions' layering_index,
6975 so we need to do a recursive diff here.
6977 vector<Command*> cmds;
6979 _session->add_commands (cmds);
6981 _session->add_command (new StatefulDiffCommand (pl));
6985 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6986 set_selected_regionview_from_region_list ((*i), Selection::Add);
6992 Editor::place_transient()
6998 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7004 samplepos_t where = get_preferred_edit_position();
7006 begin_reversible_command (_("place transient"));
7008 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7009 (*r)->region()->add_transient(where);
7012 commit_reversible_command ();
7016 Editor::remove_transient(ArdourCanvas::Item* item)
7022 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7025 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7026 _arv->remove_transient (*(float*) _line->get_data ("position"));
7030 Editor::snap_regions_to_grid ()
7032 list <boost::shared_ptr<Playlist > > used_playlists;
7034 RegionSelection rs = get_regions_from_selection_and_entered ();
7036 if (!_session || rs.empty()) {
7040 begin_reversible_command (_("snap regions to grid"));
7042 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7044 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7046 if (!pl->frozen()) {
7047 /* we haven't seen this playlist before */
7049 /* remember used playlists so we can thaw them later */
7050 used_playlists.push_back(pl);
7053 (*r)->region()->clear_changes ();
7055 MusicSample start ((*r)->region()->first_sample (), 0);
7057 (*r)->region()->set_position (start.sample, start.division);
7058 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7061 while (used_playlists.size() > 0) {
7062 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7064 used_playlists.pop_front();
7067 commit_reversible_command ();
7071 Editor::close_region_gaps ()
7073 list <boost::shared_ptr<Playlist > > used_playlists;
7075 RegionSelection rs = get_regions_from_selection_and_entered ();
7077 if (!_session || rs.empty()) {
7081 Dialog dialog (_("Close Region Gaps"));
7084 table.set_spacings (12);
7085 table.set_border_width (12);
7086 Label* l = manage (left_aligned_label (_("Crossfade length")));
7087 table.attach (*l, 0, 1, 0, 1);
7089 SpinButton spin_crossfade (1, 0);
7090 spin_crossfade.set_range (0, 15);
7091 spin_crossfade.set_increments (1, 1);
7092 spin_crossfade.set_value (5);
7093 table.attach (spin_crossfade, 1, 2, 0, 1);
7095 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7097 l = manage (left_aligned_label (_("Pull-back length")));
7098 table.attach (*l, 0, 1, 1, 2);
7100 SpinButton spin_pullback (1, 0);
7101 spin_pullback.set_range (0, 100);
7102 spin_pullback.set_increments (1, 1);
7103 spin_pullback.set_value(30);
7104 table.attach (spin_pullback, 1, 2, 1, 2);
7106 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7108 dialog.get_vbox()->pack_start (table);
7109 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7110 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7113 if (dialog.run () == RESPONSE_CANCEL) {
7117 samplepos_t crossfade_len = spin_crossfade.get_value();
7118 samplepos_t pull_back_samples = spin_pullback.get_value();
7120 crossfade_len = lrintf (crossfade_len * _session->sample_rate()/1000);
7121 pull_back_samples = lrintf (pull_back_samples * _session->sample_rate()/1000);
7123 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7125 begin_reversible_command (_("close region gaps"));
7128 boost::shared_ptr<Region> last_region;
7130 rs.sort_by_position_and_track();
7132 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7134 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7136 if (!pl->frozen()) {
7137 /* we haven't seen this playlist before */
7139 /* remember used playlists so we can thaw them later */
7140 used_playlists.push_back(pl);
7144 samplepos_t position = (*r)->region()->position();
7146 if (idx == 0 || position < last_region->position()){
7147 last_region = (*r)->region();
7152 (*r)->region()->clear_changes ();
7153 (*r)->region()->trim_front( (position - pull_back_samples));
7155 last_region->clear_changes ();
7156 last_region->trim_end( (position - pull_back_samples + crossfade_len));
7158 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7159 _session->add_command (new StatefulDiffCommand (last_region));
7161 last_region = (*r)->region();
7165 while (used_playlists.size() > 0) {
7166 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7168 used_playlists.pop_front();
7171 commit_reversible_command ();
7175 Editor::tab_to_transient (bool forward)
7177 AnalysisFeatureList positions;
7179 RegionSelection rs = get_regions_from_selection_and_entered ();
7185 samplepos_t pos = _session->audible_sample ();
7187 if (!selection->tracks.empty()) {
7189 /* don't waste time searching for transients in duplicate playlists.
7192 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7194 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7196 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7199 boost::shared_ptr<Track> tr = rtv->track();
7201 boost::shared_ptr<Playlist> pl = tr->playlist ();
7203 samplepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7206 positions.push_back (result);
7219 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7220 (*r)->region()->get_transients (positions);
7224 TransientDetector::cleanup_transients (positions, _session->sample_rate(), 3.0);
7227 AnalysisFeatureList::iterator x;
7229 for (x = positions.begin(); x != positions.end(); ++x) {
7235 if (x != positions.end ()) {
7236 _session->request_locate (*x);
7240 AnalysisFeatureList::reverse_iterator x;
7242 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7248 if (x != positions.rend ()) {
7249 _session->request_locate (*x);
7255 Editor::playhead_forward_to_grid ()
7261 MusicSample pos (playhead_cursor->current_sample (), 0);
7263 if (pos.sample < max_samplepos - 1) {
7265 snap_to_internal (pos, RoundUpAlways, false, true);
7266 _session->request_locate (pos.sample);
7272 Editor::playhead_backward_to_grid ()
7278 MusicSample pos (playhead_cursor->current_sample (), 0);
7280 if (pos.sample > 2) {
7282 snap_to_internal (pos, RoundDownAlways, false, true);
7283 _session->request_locate (pos.sample);
7288 Editor::set_track_height (Height h)
7290 TrackSelection& ts (selection->tracks);
7292 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7293 (*x)->set_height_enum (h);
7298 Editor::toggle_tracks_active ()
7300 TrackSelection& ts (selection->tracks);
7302 bool target = false;
7308 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7309 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7313 target = !rtv->_route->active();
7316 rtv->_route->set_active (target, this);
7322 Editor::remove_tracks ()
7324 /* this will delete GUI objects that may be the subject of an event
7325 handler in which this method is called. Defer actual deletion to the
7326 next idle callback, when all event handling is finished.
7328 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7332 Editor::idle_remove_tracks ()
7334 Session::StateProtector sp (_session);
7336 return false; /* do not call again */
7340 Editor::_remove_tracks ()
7342 TrackSelection& ts (selection->tracks);
7348 vector<string> choices;
7353 const char* trackstr;
7356 vector<boost::shared_ptr<Route> > routes;
7357 vector<boost::shared_ptr<VCA> > vcas;
7358 bool special_bus = false;
7360 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7361 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7363 vcas.push_back (vtv->vca());
7367 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7371 if (rtv->is_track()) {
7376 routes.push_back (rtv->_route);
7378 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7383 if (special_bus && !Config->get_allow_special_bus_removal()) {
7384 MessageDialog msg (_("That would be bad news ...."),
7388 msg.set_secondary_text (string_compose (_(
7389 "Removing the master or monitor bus is such a bad idea\n\
7390 that %1 is not going to allow it.\n\
7392 If you really want to do this sort of thing\n\
7393 edit your ardour.rc file to set the\n\
7394 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7401 if (ntracks + nbusses + nvcas == 0) {
7407 trackstr = P_("track", "tracks", ntracks);
7408 busstr = P_("bus", "busses", nbusses);
7409 vcastr = P_("VCA", "VCAs", nvcas);
7411 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7412 title = _("Remove various strips");
7413 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7414 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7416 else if (ntracks > 0 && nbusses > 0) {
7417 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7418 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7419 ntracks, trackstr, nbusses, busstr);
7421 else if (ntracks > 0 && nvcas > 0) {
7422 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7423 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7424 ntracks, trackstr, nvcas, vcastr);
7426 else if (nbusses > 0 && nvcas > 0) {
7427 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7428 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7429 nbusses, busstr, nvcas, vcastr);
7431 else if (ntracks > 0) {
7432 title = string_compose (_("Remove %1"), trackstr);
7433 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7436 else if (nbusses > 0) {
7437 title = string_compose (_("Remove %1"), busstr);
7438 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7441 else if (nvcas > 0) {
7442 title = string_compose (_("Remove %1"), vcastr);
7443 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7451 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7454 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7456 choices.push_back (_("No, do nothing."));
7457 if (ntracks + nbusses + nvcas > 1) {
7458 choices.push_back (_("Yes, remove them."));
7460 choices.push_back (_("Yes, remove it."));
7463 Choice prompter (title, prompt, choices);
7465 if (prompter.run () != 1) {
7469 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7470 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7471 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7472 * likely because deletion requires selection) this will call
7473 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7474 * It's likewise likely that the route that has just been displayed in the
7475 * Editor-Mixer will be next in line for deletion.
7477 * So simply switch to the master-bus (if present)
7479 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7480 if ((*i)->stripable ()->is_master ()) {
7481 set_selected_mixer_strip (*(*i));
7488 PresentationInfo::ChangeSuspender cs;
7489 DisplaySuspender ds;
7491 boost::shared_ptr<RouteList> rl (new RouteList);
7492 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7495 _session->remove_routes (rl);
7497 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7498 _session->vca_manager().remove_vca (*x);
7502 /* TrackSelection and RouteList leave scope,
7503 * destructors are called,
7504 * diskstream drops references, save_state is called (again for every track)
7509 Editor::do_insert_time ()
7511 if (selection->tracks.empty()) {
7515 InsertRemoveTimeDialog d (*this);
7516 int response = d.run ();
7518 if (response != RESPONSE_OK) {
7522 if (d.distance() == 0) {
7529 d.intersected_region_action (),
7533 d.move_glued_markers(),
7534 d.move_locked_markers(),
7540 Editor::insert_time (
7541 samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7542 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7546 if (Config->get_edit_mode() == Lock) {
7549 bool in_command = false;
7551 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7553 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7557 /* don't operate on any playlist more than once, which could
7558 * happen if "all playlists" is enabled, but there is more
7559 * than 1 track using playlists "from" a given track.
7562 set<boost::shared_ptr<Playlist> > pl;
7564 if (all_playlists) {
7565 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7566 if (rtav && rtav->track ()) {
7567 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7568 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7573 if ((*x)->playlist ()) {
7574 pl.insert ((*x)->playlist ());
7578 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7580 (*i)->clear_changes ();
7581 (*i)->clear_owned_changes ();
7584 begin_reversible_command (_("insert time"));
7588 if (opt == SplitIntersected) {
7589 /* non musical split */
7590 (*i)->split (MusicSample (pos, 0));
7593 (*i)->shift (pos, samples, (opt == MoveIntersected), ignore_music_glue);
7595 vector<Command*> cmds;
7597 _session->add_commands (cmds);
7599 _session->add_command (new StatefulDiffCommand (*i));
7603 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7606 begin_reversible_command (_("insert time"));
7609 rtav->route ()->shift (pos, samples);
7616 const int32_t divisions = get_grid_music_divisions (0);
7617 XMLNode& before (_session->locations()->get_state());
7618 Locations::LocationList copy (_session->locations()->list());
7620 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7622 Locations::LocationList::const_iterator tmp;
7624 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7625 bool const was_locked = (*i)->locked ();
7626 if (locked_markers_too) {
7630 if ((*i)->start() >= pos) {
7631 // move end first, in case we're moving by more than the length of the range
7632 if (!(*i)->is_mark()) {
7633 (*i)->set_end ((*i)->end() + samples, false, true, divisions);
7635 (*i)->set_start ((*i)->start() + samples, false, true, divisions);
7647 begin_reversible_command (_("insert time"));
7650 XMLNode& after (_session->locations()->get_state());
7651 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7657 begin_reversible_command (_("insert time"));
7660 XMLNode& before (_session->tempo_map().get_state());
7661 _session->tempo_map().insert_time (pos, samples);
7662 XMLNode& after (_session->tempo_map().get_state());
7663 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7667 commit_reversible_command ();
7672 Editor::do_remove_time ()
7674 if (selection->tracks.empty()) {
7678 InsertRemoveTimeDialog d (*this, true);
7680 int response = d.run ();
7682 if (response != RESPONSE_OK) {
7686 samplecnt_t distance = d.distance();
7688 if (distance == 0) {
7698 d.move_glued_markers(),
7699 d.move_locked_markers(),
7705 Editor::remove_time (samplepos_t pos, samplecnt_t samples, InsertTimeOption opt,
7706 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7708 if (Config->get_edit_mode() == Lock) {
7709 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7712 bool in_command = false;
7714 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7716 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7720 XMLNode &before = pl->get_state();
7723 begin_reversible_command (_("remove time"));
7727 std::list<AudioRange> rl;
7728 AudioRange ar(pos, pos+samples, 0);
7731 pl->shift (pos, -samples, true, ignore_music_glue);
7733 XMLNode &after = pl->get_state();
7735 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7739 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7742 begin_reversible_command (_("remove time"));
7745 rtav->route ()->shift (pos, -samples);
7749 const int32_t divisions = get_grid_music_divisions (0);
7750 std::list<Location*> loc_kill_list;
7755 XMLNode& before (_session->locations()->get_state());
7756 Locations::LocationList copy (_session->locations()->list());
7758 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7759 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7761 bool const was_locked = (*i)->locked ();
7762 if (locked_markers_too) {
7766 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7767 if ((*i)->end() >= pos
7768 && (*i)->end() < pos+samples
7769 && (*i)->start() >= pos
7770 && (*i)->end() < pos+samples) { // range is completely enclosed; kill it
7772 loc_kill_list.push_back(*i);
7773 } else { // only start or end is included, try to do the right thing
7774 // move start before moving end, to avoid trying to move the end to before the start
7775 // if we're removing more time than the length of the range
7776 if ((*i)->start() >= pos && (*i)->start() < pos+samples) {
7777 // start is within cut
7778 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7780 } else if ((*i)->start() >= pos+samples) {
7781 // start (and thus entire range) lies beyond end of cut
7782 (*i)->set_start ((*i)->start() - samples, false, true, divisions); // slip the start marker back
7785 if ((*i)->end() >= pos && (*i)->end() < pos+samples) {
7786 // end is inside cut
7787 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7789 } else if ((*i)->end() >= pos+samples) {
7790 // end is beyond end of cut
7791 (*i)->set_end ((*i)->end() - samples, false, true, divisions); // slip the end marker back
7796 } else if ((*i)->start() >= pos && (*i)->start() < pos+samples ) {
7797 loc_kill_list.push_back(*i);
7799 } else if ((*i)->start() >= pos) {
7800 (*i)->set_start ((*i)->start() -samples, false, true, divisions);
7810 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7811 _session->locations()->remove( *i );
7816 begin_reversible_command (_("remove time"));
7819 XMLNode& after (_session->locations()->get_state());
7820 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7825 XMLNode& before (_session->tempo_map().get_state());
7827 if (_session->tempo_map().remove_time (pos, samples) ) {
7829 begin_reversible_command (_("remove time"));
7832 XMLNode& after (_session->tempo_map().get_state());
7833 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7838 commit_reversible_command ();
7843 Editor::fit_selection ()
7845 if (!selection->tracks.empty()) {
7846 fit_tracks (selection->tracks);
7850 /* no selected tracks - use tracks with selected regions */
7852 if (!selection->regions.empty()) {
7853 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7854 tvl.push_back (&(*r)->get_time_axis_view ());
7860 } else if (internal_editing()) {
7861 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7864 if (entered_track) {
7865 tvl.push_back (entered_track);
7873 Editor::fit_tracks (TrackViewList & tracks)
7875 if (tracks.empty()) {
7879 uint32_t child_heights = 0;
7880 int visible_tracks = 0;
7882 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7884 if (!(*t)->marked_for_display()) {
7888 child_heights += (*t)->effective_height() - (*t)->current_height();
7892 /* compute the per-track height from:
7894 * total canvas visible height
7895 * - height that will be taken by visible children of selected tracks
7896 * - height of the ruler/hscroll area
7898 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7899 double first_y_pos = DBL_MAX;
7901 if (h < TimeAxisView::preset_height (HeightSmall)) {
7902 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7903 /* too small to be displayed */
7907 undo_visual_stack.push_back (current_visual_state (true));
7908 PBD::Unwinder<bool> nsv (no_save_visual, true);
7910 /* build a list of all tracks, including children */
7913 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7915 TimeAxisView::Children c = (*i)->get_child_list ();
7916 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7917 all.push_back (j->get());
7922 // find selection range.
7923 // if someone knows how to user TrackViewList::iterator for this
7925 int selected_top = -1;
7926 int selected_bottom = -1;
7928 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7929 if ((*t)->marked_for_display ()) {
7930 if (tracks.contains(*t)) {
7931 if (selected_top == -1) {
7934 selected_bottom = i;
7940 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7941 if ((*t)->marked_for_display ()) {
7942 if (tracks.contains(*t)) {
7943 (*t)->set_height (h);
7944 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7946 if (i > selected_top && i < selected_bottom) {
7947 hide_track_in_display (*t);
7954 set the controls_layout height now, because waiting for its size
7955 request signal handler will cause the vertical adjustment setting to fail
7958 controls_layout.property_height () = _full_canvas_height;
7959 vertical_adjustment.set_value (first_y_pos);
7961 redo_visual_stack.push_back (current_visual_state (true));
7963 visible_tracks_selector.set_text (_("Sel"));
7967 Editor::save_visual_state (uint32_t n)
7969 while (visual_states.size() <= n) {
7970 visual_states.push_back (0);
7973 if (visual_states[n] != 0) {
7974 delete visual_states[n];
7977 visual_states[n] = current_visual_state (true);
7982 Editor::goto_visual_state (uint32_t n)
7984 if (visual_states.size() <= n) {
7988 if (visual_states[n] == 0) {
7992 use_visual_state (*visual_states[n]);
7996 Editor::start_visual_state_op (uint32_t n)
7998 save_visual_state (n);
8000 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8002 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8003 pup->set_text (buf);
8008 Editor::cancel_visual_state_op (uint32_t n)
8010 goto_visual_state (n);
8014 Editor::toggle_region_mute ()
8016 if (_ignore_region_action) {
8020 RegionSelection rs = get_regions_from_selection_and_entered ();
8026 if (rs.size() > 1) {
8027 begin_reversible_command (_("mute regions"));
8029 begin_reversible_command (_("mute region"));
8032 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8034 (*i)->region()->playlist()->clear_changes ();
8035 (*i)->region()->set_muted (!(*i)->region()->muted ());
8036 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8040 commit_reversible_command ();
8044 Editor::combine_regions ()
8046 /* foreach track with selected regions, take all selected regions
8047 and join them into a new region containing the subregions (as a
8051 typedef set<RouteTimeAxisView*> RTVS;
8054 if (selection->regions.empty()) {
8058 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8059 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8062 tracks.insert (rtv);
8066 begin_reversible_command (_("combine regions"));
8068 vector<RegionView*> new_selection;
8070 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8073 if ((rv = (*i)->combine_regions ()) != 0) {
8074 new_selection.push_back (rv);
8078 selection->clear_regions ();
8079 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8080 selection->add (*i);
8083 commit_reversible_command ();
8087 Editor::uncombine_regions ()
8089 typedef set<RouteTimeAxisView*> RTVS;
8092 if (selection->regions.empty()) {
8096 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8097 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8100 tracks.insert (rtv);
8104 begin_reversible_command (_("uncombine regions"));
8106 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8107 (*i)->uncombine_regions ();
8110 commit_reversible_command ();
8114 Editor::toggle_midi_input_active (bool flip_others)
8117 boost::shared_ptr<RouteList> rl (new RouteList);
8119 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8120 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8126 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8129 rl->push_back (rtav->route());
8130 onoff = !mt->input_active();
8134 _session->set_exclusive_input_active (rl, onoff, flip_others);
8137 static bool ok_fine (GdkEventAny*) { return true; }
8143 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8145 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8146 lock_dialog->get_vbox()->pack_start (*padlock);
8147 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8149 ArdourButton* b = manage (new ArdourButton);
8150 b->set_name ("lock button");
8151 b->set_text (_("Click to unlock"));
8152 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8153 lock_dialog->get_vbox()->pack_start (*b);
8155 lock_dialog->get_vbox()->show_all ();
8156 lock_dialog->set_size_request (200, 200);
8159 delete _main_menu_disabler;
8160 _main_menu_disabler = new MainMenuDisabler;
8162 lock_dialog->present ();
8164 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8170 lock_dialog->hide ();
8172 delete _main_menu_disabler;
8173 _main_menu_disabler = 0;
8175 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8176 start_lock_event_timing ();
8181 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8183 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8187 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8189 Timers::TimerSuspender t;
8190 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8191 Gtkmm2ext::UI::instance()->flush_pending (1);
8195 Editor::bring_all_sources_into_session ()
8202 ArdourDialog w (_("Moving embedded files into session folder"));
8203 w.get_vbox()->pack_start (msg);
8206 /* flush all pending GUI events because we're about to start copying
8210 Timers::TimerSuspender t;
8211 Gtkmm2ext::UI::instance()->flush_pending (3);
8215 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));