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 */
30 #include "pbd/error.h"
31 #include "pbd/basename.h"
32 #include "pbd/pthread_utils.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/unwind.h"
35 #include "pbd/whitespace.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/choice.h>
40 #include <gtkmm2ext/popup.h>
42 #include "ardour/audio_track.h"
43 #include "ardour/audioregion.h"
44 #include "ardour/dB.h"
45 #include "ardour/location.h"
46 #include "ardour/midi_region.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist_factory.h"
49 #include "ardour/quantize.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/reverse.h"
52 #include "ardour/session.h"
53 #include "ardour/session_playlists.h"
54 #include "ardour/strip_silence.h"
55 #include "ardour/transient_detector.h"
57 #include "ardour_ui.h"
60 #include "time_axis_view.h"
61 #include "route_time_axis.h"
62 #include "audio_time_axis.h"
63 #include "automation_time_axis.h"
64 #include "control_point.h"
65 #include "streamview.h"
66 #include "audio_streamview.h"
67 #include "audio_region_view.h"
68 #include "midi_region_view.h"
69 #include "rgb_macros.h"
70 #include "selection_templates.h"
71 #include "selection.h"
73 #include "gtk-custom-hruler.h"
74 #include "gui_thread.h"
77 #include "editor_drag.h"
78 #include "strip_silence_dialog.h"
79 #include "editor_routes.h"
80 #include "editor_regions.h"
81 #include "quantize_dialog.h"
82 #include "interthread_progress_window.h"
83 #include "insert_time_dialog.h"
84 #include "normalize_dialog.h"
85 #include "editor_cursors.h"
86 #include "mouse_cursors.h"
87 #include "patch_change_dialog.h"
88 #include "transpose_dialog.h"
93 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
98 using Gtkmm2ext::Keyboard;
100 /***********************************************************************
102 ***********************************************************************/
105 Editor::undo (uint32_t n)
107 if (_drags->active ()) {
117 Editor::redo (uint32_t n)
119 if (_drags->active ()) {
129 Editor::split_regions_at (framepos_t where, RegionSelection& regions)
133 list <boost::shared_ptr<Playlist > > used_playlists;
135 if (regions.empty()) {
139 begin_reversible_command (_("split"));
141 // if splitting a single region, and snap-to is using
142 // region boundaries, don't pay attention to them
144 if (regions.size() == 1) {
145 switch (_snap_type) {
146 case SnapToRegionStart:
147 case SnapToRegionSync:
148 case SnapToRegionEnd:
157 EditorFreeze(); /* Emit Signal */
160 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
162 RegionSelection::iterator tmp;
164 /* XXX this test needs to be more complicated, to make sure we really
165 have something to split.
168 if (!(*a)->region()->covers (where)) {
176 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
184 /* we haven't seen this playlist before */
186 /* remember used playlists so we can thaw them later */
187 used_playlists.push_back(pl);
192 pl->clear_changes ();
193 pl->split_region ((*a)->region(), where);
194 _session->add_command (new StatefulDiffCommand (pl));
200 while (used_playlists.size() > 0) {
201 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
203 used_playlists.pop_front();
206 commit_reversible_command ();
209 EditorThaw(); /* Emit Signal */
213 /** Move one extreme of the current range selection. If more than one range is selected,
214 * the start of the earliest range or the end of the latest range is moved.
216 * @param move_end true to move the end of the current range selection, false to move
218 * @param next true to move the extreme to the next region boundary, false to move to
222 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
224 if (selection->time.start() == selection->time.end_frame()) {
228 framepos_t start = selection->time.start ();
229 framepos_t end = selection->time.end_frame ();
231 /* the position of the thing we may move */
232 framepos_t pos = move_end ? end : start;
233 int dir = next ? 1 : -1;
235 /* so we don't find the current region again */
236 if (dir > 0 || pos > 0) {
240 framepos_t const target = get_region_boundary (pos, dir, true, false);
255 begin_reversible_command (_("alter selection"));
256 selection->set_preserving_all_ranges (start, end);
257 commit_reversible_command ();
261 Editor::nudge_forward_release (GdkEventButton* ev)
263 if (ev->state & Keyboard::PrimaryModifier) {
264 nudge_forward (false, true);
266 nudge_forward (false, false);
272 Editor::nudge_backward_release (GdkEventButton* ev)
274 if (ev->state & Keyboard::PrimaryModifier) {
275 nudge_backward (false, true);
277 nudge_backward (false, false);
284 Editor::nudge_forward (bool next, bool force_playhead)
287 framepos_t next_distance;
293 RegionSelection rs = get_regions_from_selection_and_entered ();
295 if (!force_playhead && !rs.empty()) {
297 begin_reversible_command (_("nudge regions forward"));
299 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
300 boost::shared_ptr<Region> r ((*i)->region());
302 distance = get_nudge_distance (r->position(), next_distance);
305 distance = next_distance;
309 r->set_position (r->position() + distance);
310 _session->add_command (new StatefulDiffCommand (r));
313 commit_reversible_command ();
316 } else if (!force_playhead && !selection->markers.empty()) {
320 begin_reversible_command (_("nudge location forward"));
322 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
324 Location* loc = find_location_from_marker ((*i), is_start);
328 XMLNode& before (loc->get_state());
331 distance = get_nudge_distance (loc->start(), next_distance);
333 distance = next_distance;
335 if (max_framepos - distance > loc->start() + loc->length()) {
336 loc->set_start (loc->start() + distance);
338 loc->set_start (max_framepos - loc->length());
341 distance = get_nudge_distance (loc->end(), next_distance);
343 distance = next_distance;
345 if (max_framepos - distance > loc->end()) {
346 loc->set_end (loc->end() + distance);
348 loc->set_end (max_framepos);
351 XMLNode& after (loc->get_state());
352 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
356 commit_reversible_command ();
359 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
360 _session->request_locate (playhead_cursor->current_frame + distance);
365 Editor::nudge_backward (bool next, bool force_playhead)
368 framepos_t next_distance;
374 RegionSelection rs = get_regions_from_selection_and_entered ();
376 if (!force_playhead && !rs.empty()) {
378 begin_reversible_command (_("nudge regions backward"));
380 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
381 boost::shared_ptr<Region> r ((*i)->region());
383 distance = get_nudge_distance (r->position(), next_distance);
386 distance = next_distance;
391 if (r->position() > distance) {
392 r->set_position (r->position() - distance);
396 _session->add_command (new StatefulDiffCommand (r));
399 commit_reversible_command ();
401 } else if (!force_playhead && !selection->markers.empty()) {
405 begin_reversible_command (_("nudge location forward"));
407 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
409 Location* loc = find_location_from_marker ((*i), is_start);
413 XMLNode& before (loc->get_state());
416 distance = get_nudge_distance (loc->start(), next_distance);
418 distance = next_distance;
420 if (distance < loc->start()) {
421 loc->set_start (loc->start() - distance);
426 distance = get_nudge_distance (loc->end(), next_distance);
429 distance = next_distance;
432 if (distance < loc->end() - loc->length()) {
433 loc->set_end (loc->end() - distance);
435 loc->set_end (loc->length());
439 XMLNode& after (loc->get_state());
440 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
444 commit_reversible_command ();
448 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
450 if (playhead_cursor->current_frame > distance) {
451 _session->request_locate (playhead_cursor->current_frame - distance);
453 _session->goto_start();
459 Editor::nudge_forward_capture_offset ()
461 RegionSelection rs = get_regions_from_selection_and_entered ();
463 if (!_session || rs.empty()) {
467 begin_reversible_command (_("nudge forward"));
469 framepos_t const distance = _session->worst_output_latency();
471 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
472 boost::shared_ptr<Region> r ((*i)->region());
475 r->set_position (r->position() + distance);
476 _session->add_command(new StatefulDiffCommand (r));
479 commit_reversible_command ();
483 Editor::nudge_backward_capture_offset ()
485 RegionSelection rs = get_regions_from_selection_and_entered ();
487 if (!_session || rs.empty()) {
491 begin_reversible_command (_("nudge backward"));
493 framepos_t const distance = _session->worst_output_latency();
495 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
496 boost::shared_ptr<Region> r ((*i)->region());
500 if (r->position() > distance) {
501 r->set_position (r->position() - distance);
505 _session->add_command(new StatefulDiffCommand (r));
508 commit_reversible_command ();
514 Editor::move_to_start ()
516 _session->goto_start ();
520 Editor::move_to_end ()
523 _session->request_locate (_session->current_end_frame());
527 Editor::build_region_boundary_cache ()
530 vector<RegionPoint> interesting_points;
531 boost::shared_ptr<Region> r;
532 TrackViewList tracks;
535 region_boundary_cache.clear ();
541 switch (_snap_type) {
542 case SnapToRegionStart:
543 interesting_points.push_back (Start);
545 case SnapToRegionEnd:
546 interesting_points.push_back (End);
548 case SnapToRegionSync:
549 interesting_points.push_back (SyncPoint);
551 case SnapToRegionBoundary:
552 interesting_points.push_back (Start);
553 interesting_points.push_back (End);
556 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
561 TimeAxisView *ontrack = 0;
564 if (!selection->tracks.empty()) {
565 tlist = selection->tracks.filter_to_unique_playlists ();
567 tlist = track_views.filter_to_unique_playlists ();
570 while (pos < _session->current_end_frame() && !at_end) {
573 framepos_t lpos = max_framepos;
575 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
577 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
578 if (*p == interesting_points.back()) {
581 /* move to next point type */
587 rpos = r->first_frame();
591 rpos = r->last_frame();
595 rpos = r->sync_position ();
603 RouteTimeAxisView *rtav;
605 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
606 if (rtav->track() != 0) {
607 speed = rtav->track()->speed();
611 rpos = track_frame_to_session_frame (rpos, speed);
617 /* prevent duplicates, but we don't use set<> because we want to be able
621 vector<framepos_t>::iterator ri;
623 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
629 if (ri == region_boundary_cache.end()) {
630 region_boundary_cache.push_back (rpos);
637 /* finally sort to be sure that the order is correct */
639 sort (region_boundary_cache.begin(), region_boundary_cache.end());
642 boost::shared_ptr<Region>
643 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
645 TrackViewList::iterator i;
646 framepos_t closest = max_framepos;
647 boost::shared_ptr<Region> ret;
651 framepos_t track_frame;
652 RouteTimeAxisView *rtav;
654 for (i = tracks.begin(); i != tracks.end(); ++i) {
657 boost::shared_ptr<Region> r;
660 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
661 if (rtav->track()!=0)
662 track_speed = rtav->track()->speed();
665 track_frame = session_frame_to_track_frame(frame, track_speed);
667 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
673 rpos = r->first_frame ();
677 rpos = r->last_frame ();
681 rpos = r->sync_position ();
685 // rpos is a "track frame", converting it to "_session frame"
686 rpos = track_frame_to_session_frame(rpos, track_speed);
689 distance = rpos - frame;
691 distance = frame - rpos;
694 if (distance < closest) {
706 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
708 framecnt_t distance = max_framepos;
709 framepos_t current_nearest = -1;
711 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
712 framepos_t contender;
715 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
721 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
725 d = ::llabs (pos - contender);
728 current_nearest = contender;
733 return current_nearest;
737 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
742 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
744 if (!selection->tracks.empty()) {
746 target = find_next_region_boundary (pos, dir, selection->tracks);
750 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
751 get_onscreen_tracks (tvl);
752 target = find_next_region_boundary (pos, dir, tvl);
754 target = find_next_region_boundary (pos, dir, track_views);
760 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
761 get_onscreen_tracks (tvl);
762 target = find_next_region_boundary (pos, dir, tvl);
764 target = find_next_region_boundary (pos, dir, track_views);
772 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
774 framepos_t pos = playhead_cursor->current_frame;
781 // so we don't find the current region again..
782 if (dir > 0 || pos > 0) {
786 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
790 _session->request_locate (target);
794 Editor::cursor_to_next_region_boundary (bool with_selection)
796 cursor_to_region_boundary (with_selection, 1);
800 Editor::cursor_to_previous_region_boundary (bool with_selection)
802 cursor_to_region_boundary (with_selection, -1);
806 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
808 boost::shared_ptr<Region> r;
809 framepos_t pos = cursor->current_frame;
815 TimeAxisView *ontrack = 0;
817 // so we don't find the current region again..
821 if (!selection->tracks.empty()) {
823 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
825 } else if (clicked_axisview) {
828 t.push_back (clicked_axisview);
830 r = find_next_region (pos, point, dir, t, &ontrack);
834 r = find_next_region (pos, point, dir, track_views, &ontrack);
843 pos = r->first_frame ();
847 pos = r->last_frame ();
851 pos = r->sync_position ();
856 RouteTimeAxisView *rtav;
858 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
859 if (rtav->track() != 0) {
860 speed = rtav->track()->speed();
864 pos = track_frame_to_session_frame(pos, speed);
866 if (cursor == playhead_cursor) {
867 _session->request_locate (pos);
869 cursor->set_position (pos);
874 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
876 cursor_to_region_point (cursor, point, 1);
880 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
882 cursor_to_region_point (cursor, point, -1);
886 Editor::cursor_to_selection_start (EditorCursor *cursor)
890 switch (mouse_mode) {
892 if (!selection->regions.empty()) {
893 pos = selection->regions.start();
898 if (!selection->time.empty()) {
899 pos = selection->time.start ();
907 if (cursor == playhead_cursor) {
908 _session->request_locate (pos);
910 cursor->set_position (pos);
915 Editor::cursor_to_selection_end (EditorCursor *cursor)
919 switch (mouse_mode) {
921 if (!selection->regions.empty()) {
922 pos = selection->regions.end_frame();
927 if (!selection->time.empty()) {
928 pos = selection->time.end_frame ();
936 if (cursor == playhead_cursor) {
937 _session->request_locate (pos);
939 cursor->set_position (pos);
944 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
954 if (selection->markers.empty()) {
958 if (!mouse_frame (mouse, ignored)) {
962 add_location_mark (mouse);
965 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
969 framepos_t pos = loc->start();
971 // so we don't find the current region again..
972 if (dir > 0 || pos > 0) {
976 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
980 loc->move_to (target);
984 Editor::selected_marker_to_next_region_boundary (bool with_selection)
986 selected_marker_to_region_boundary (with_selection, 1);
990 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
992 selected_marker_to_region_boundary (with_selection, -1);
996 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
998 boost::shared_ptr<Region> r;
1003 if (!_session || selection->markers.empty()) {
1007 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1011 TimeAxisView *ontrack = 0;
1015 // so we don't find the current region again..
1019 if (!selection->tracks.empty()) {
1021 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1025 r = find_next_region (pos, point, dir, track_views, &ontrack);
1034 pos = r->first_frame ();
1038 pos = r->last_frame ();
1042 pos = r->adjust_to_sync (r->first_frame());
1047 RouteTimeAxisView *rtav;
1049 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1050 if (rtav->track() != 0) {
1051 speed = rtav->track()->speed();
1055 pos = track_frame_to_session_frame(pos, speed);
1061 Editor::selected_marker_to_next_region_point (RegionPoint point)
1063 selected_marker_to_region_point (point, 1);
1067 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1069 selected_marker_to_region_point (point, -1);
1073 Editor::selected_marker_to_selection_start ()
1079 if (!_session || selection->markers.empty()) {
1083 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1087 switch (mouse_mode) {
1089 if (!selection->regions.empty()) {
1090 pos = selection->regions.start();
1095 if (!selection->time.empty()) {
1096 pos = selection->time.start ();
1108 Editor::selected_marker_to_selection_end ()
1114 if (!_session || selection->markers.empty()) {
1118 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1122 switch (mouse_mode) {
1124 if (!selection->regions.empty()) {
1125 pos = selection->regions.end_frame();
1130 if (!selection->time.empty()) {
1131 pos = selection->time.end_frame ();
1143 Editor::scroll_playhead (bool forward)
1145 framepos_t pos = playhead_cursor->current_frame;
1146 framecnt_t delta = (framecnt_t) floor (current_page_frames() / 0.8);
1149 if (pos == max_framepos) {
1153 if (pos < max_framepos - delta) {
1172 _session->request_locate (pos);
1176 Editor::cursor_align (bool playhead_to_edit)
1182 if (playhead_to_edit) {
1184 if (selection->markers.empty()) {
1188 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1191 /* move selected markers to playhead */
1193 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1196 Location* loc = find_location_from_marker (*i, ignored);
1198 if (loc->is_mark()) {
1199 loc->set_start (playhead_cursor->current_frame);
1201 loc->set (playhead_cursor->current_frame,
1202 playhead_cursor->current_frame + loc->length());
1209 Editor::scroll_backward (float pages)
1211 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1212 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1215 if (leftmost_frame < cnt) {
1218 frame = leftmost_frame - cnt;
1221 reset_x_origin (frame);
1225 Editor::scroll_forward (float pages)
1227 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1228 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1231 if (max_framepos - cnt < leftmost_frame) {
1232 frame = max_framepos - cnt;
1234 frame = leftmost_frame + cnt;
1237 reset_x_origin (frame);
1241 Editor::scroll_tracks_down ()
1243 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1244 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1245 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1248 vertical_adjustment.set_value (vert_value);
1252 Editor::scroll_tracks_up ()
1254 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1258 Editor::scroll_tracks_down_line ()
1260 double vert_value = vertical_adjustment.get_value() + 60;
1262 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1263 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1266 vertical_adjustment.set_value (vert_value);
1270 Editor::scroll_tracks_up_line ()
1272 reset_y_origin (vertical_adjustment.get_value() - 60);
1278 Editor::tav_zoom_step (bool coarser)
1280 _routes->suspend_redisplay ();
1284 if (selection->tracks.empty()) {
1287 ts = &selection->tracks;
1290 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1291 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1292 tv->step_height (coarser);
1295 _routes->resume_redisplay ();
1299 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1301 _routes->suspend_redisplay ();
1305 if (selection->tracks.empty() || force_all) {
1308 ts = &selection->tracks;
1311 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1312 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1313 uint32_t h = tv->current_height ();
1318 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1323 tv->set_height (h + 5);
1327 _routes->resume_redisplay ();
1331 Editor::temporal_zoom_step (bool coarser)
1333 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1337 nfpu = frames_per_unit;
1342 nfpu = max(1.0,(nfpu/1.61803399));
1345 temporal_zoom (nfpu);
1349 Editor::temporal_zoom (gdouble fpu)
1351 if (!_session) return;
1353 framepos_t current_page = current_page_frames();
1354 framepos_t current_leftmost = leftmost_frame;
1355 framepos_t current_rightmost;
1356 framepos_t current_center;
1357 framepos_t new_page_size;
1358 framepos_t half_page_size;
1359 framepos_t leftmost_after_zoom = 0;
1361 bool in_track_canvas;
1365 /* XXX this limit is also in ::set_frames_per_unit() */
1367 if (frames_per_unit <= 1.0 && fpu <= frames_per_unit) {
1373 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1374 // segfaults for lack of memory. If somebody decides this is not high enough I
1375 // believe it can be raisen to higher values but some limit must be in place.
1380 new_page_size = (framepos_t) floor (_canvas_width * nfpu);
1381 half_page_size = new_page_size / 2;
1383 switch (zoom_focus) {
1385 leftmost_after_zoom = current_leftmost;
1388 case ZoomFocusRight:
1389 current_rightmost = leftmost_frame + current_page;
1390 if (current_rightmost < new_page_size) {
1391 leftmost_after_zoom = 0;
1393 leftmost_after_zoom = current_rightmost - new_page_size;
1397 case ZoomFocusCenter:
1398 current_center = current_leftmost + (current_page/2);
1399 if (current_center < half_page_size) {
1400 leftmost_after_zoom = 0;
1402 leftmost_after_zoom = current_center - half_page_size;
1406 case ZoomFocusPlayhead:
1407 /* centre playhead */
1408 l = playhead_cursor->current_frame - (new_page_size * 0.5);
1411 leftmost_after_zoom = 0;
1412 } else if (l > max_framepos) {
1413 leftmost_after_zoom = max_framepos - new_page_size;
1415 leftmost_after_zoom = (framepos_t) l;
1419 case ZoomFocusMouse:
1420 /* try to keep the mouse over the same point in the display */
1422 if (!mouse_frame (where, in_track_canvas)) {
1423 /* use playhead instead */
1424 where = playhead_cursor->current_frame;
1426 if (where < half_page_size) {
1427 leftmost_after_zoom = 0;
1429 leftmost_after_zoom = where - half_page_size;
1434 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1437 leftmost_after_zoom = 0;
1438 } else if (l > max_framepos) {
1439 leftmost_after_zoom = max_framepos - new_page_size;
1441 leftmost_after_zoom = (framepos_t) l;
1448 /* try to keep the edit point in the same place */
1449 where = get_preferred_edit_position ();
1453 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1456 leftmost_after_zoom = 0;
1457 } else if (l > max_framepos) {
1458 leftmost_after_zoom = max_framepos - new_page_size;
1460 leftmost_after_zoom = (framepos_t) l;
1464 /* edit point not defined */
1471 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1473 reposition_and_zoom (leftmost_after_zoom, nfpu);
1477 Editor::temporal_zoom_region (bool both_axes)
1479 framepos_t start = max_framepos;
1481 set<TimeAxisView*> tracks;
1483 RegionSelection rs = get_regions_from_selection_and_entered ();
1489 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1491 if ((*i)->region()->position() < start) {
1492 start = (*i)->region()->position();
1495 if ((*i)->region()->last_frame() + 1 > end) {
1496 end = (*i)->region()->last_frame() + 1;
1499 tracks.insert (&((*i)->get_time_axis_view()));
1502 /* now comes an "interesting" hack ... make sure we leave a little space
1503 at each end of the editor so that the zoom doesn't fit the region
1504 precisely to the screen.
1507 GdkScreen* screen = gdk_screen_get_default ();
1508 gint pixwidth = gdk_screen_get_width (screen);
1509 gint mmwidth = gdk_screen_get_width_mm (screen);
1510 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1511 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1513 if ((start == 0 && end == 0) || end < start) {
1517 framepos_t range = end - start;
1518 double new_fpu = (double)range / (double)_canvas_width;
1519 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
1521 if (start > extra_samples) {
1522 start -= extra_samples;
1527 if (max_framepos - extra_samples > end) {
1528 end += extra_samples;
1533 /* if we're zooming on both axes we need to save track heights etc.
1536 undo_visual_stack.push_back (current_visual_state (both_axes));
1538 PBD::Unwinder<bool> nsv (no_save_visual, true);
1540 temporal_zoom_by_frame (start, end);
1543 uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1545 /* set visible track heights appropriately */
1547 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1548 (*t)->set_height (per_track_height);
1551 /* hide irrelevant tracks */
1553 _routes->suspend_redisplay ();
1555 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1556 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1557 hide_track_in_display (*i);
1561 _routes->resume_redisplay ();
1563 vertical_adjustment.set_value (0.0);
1566 redo_visual_stack.push_back (current_visual_state (both_axes));
1570 Editor::zoom_to_region (bool both_axes)
1572 temporal_zoom_region (both_axes);
1576 Editor::temporal_zoom_selection ()
1578 if (!selection) return;
1580 if (selection->time.empty()) {
1584 framepos_t start = selection->time[clicked_selection].start;
1585 framepos_t end = selection->time[clicked_selection].end;
1587 temporal_zoom_by_frame (start, end);
1591 Editor::temporal_zoom_session ()
1593 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1596 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1597 double s = _session->current_start_frame() - l * 0.01;
1601 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1602 temporal_zoom_by_frame (framecnt_t (s), e);
1607 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1609 if (!_session) return;
1611 if ((start == 0 && end == 0) || end < start) {
1615 framepos_t range = end - start;
1617 double new_fpu = (double)range / (double)_canvas_width;
1619 framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
1620 framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
1621 framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
1623 if (new_leftmost > middle) {
1627 if (new_leftmost < 0) {
1631 reposition_and_zoom (new_leftmost, new_fpu);
1635 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1640 double range_before = frame - leftmost_frame;
1643 new_fpu = frames_per_unit;
1646 new_fpu *= 1.61803399;
1647 range_before *= 1.61803399;
1649 new_fpu = max(1.0,(new_fpu/1.61803399));
1650 range_before /= 1.61803399;
1653 if (new_fpu == frames_per_unit) {
1657 framepos_t new_leftmost = frame - (framepos_t)range_before;
1659 if (new_leftmost > frame) {
1663 if (new_leftmost < 0) {
1667 reposition_and_zoom (new_leftmost, new_fpu);
1672 Editor::choose_new_marker_name(string &name) {
1674 if (!Config->get_name_new_markers()) {
1675 /* don't prompt user for a new name */
1679 ArdourPrompter dialog (true);
1681 dialog.set_prompt (_("New Name:"));
1683 dialog.set_title (_("New Location Marker"));
1685 dialog.set_name ("MarkNameWindow");
1686 dialog.set_size_request (250, -1);
1687 dialog.set_position (Gtk::WIN_POS_MOUSE);
1689 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1690 dialog.set_initial_text (name);
1694 switch (dialog.run ()) {
1695 case RESPONSE_ACCEPT:
1701 dialog.get_result(name);
1708 Editor::add_location_from_selection ()
1712 if (selection->time.empty()) {
1716 if (_session == 0 || clicked_axisview == 0) {
1720 framepos_t start = selection->time[clicked_selection].start;
1721 framepos_t end = selection->time[clicked_selection].end;
1723 _session->locations()->next_available_name(rangename,"selection");
1724 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1726 _session->begin_reversible_command (_("add marker"));
1727 XMLNode &before = _session->locations()->get_state();
1728 _session->locations()->add (location, true);
1729 XMLNode &after = _session->locations()->get_state();
1730 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1731 _session->commit_reversible_command ();
1735 Editor::add_location_mark (framepos_t where)
1739 select_new_marker = true;
1741 _session->locations()->next_available_name(markername,"mark");
1742 if (!choose_new_marker_name(markername)) {
1745 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1746 _session->begin_reversible_command (_("add marker"));
1747 XMLNode &before = _session->locations()->get_state();
1748 _session->locations()->add (location, true);
1749 XMLNode &after = _session->locations()->get_state();
1750 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1751 _session->commit_reversible_command ();
1755 Editor::add_location_from_playhead_cursor ()
1757 add_location_mark (_session->audible_frame());
1760 /** Add a range marker around each selected region */
1762 Editor::add_locations_from_region ()
1764 RegionSelection rs = get_regions_from_selection_and_entered ();
1770 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1771 XMLNode &before = _session->locations()->get_state();
1773 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1775 boost::shared_ptr<Region> region = (*i)->region ();
1777 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1779 _session->locations()->add (location, true);
1782 XMLNode &after = _session->locations()->get_state();
1783 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1784 _session->commit_reversible_command ();
1787 /** Add a single range marker around all selected regions */
1789 Editor::add_location_from_region ()
1791 RegionSelection rs = get_regions_from_selection_and_entered ();
1797 _session->begin_reversible_command (_("add marker"));
1798 XMLNode &before = _session->locations()->get_state();
1802 if (rs.size() > 1) {
1803 _session->locations()->next_available_name(markername, "regions");
1805 RegionView* rv = *(rs.begin());
1806 boost::shared_ptr<Region> region = rv->region();
1807 markername = region->name();
1810 if (!choose_new_marker_name(markername)) {
1814 // single range spanning all selected
1815 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1816 _session->locations()->add (location, true);
1818 XMLNode &after = _session->locations()->get_state();
1819 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1820 _session->commit_reversible_command ();
1826 Editor::jump_forward_to_mark ()
1832 Location *location = _session->locations()->first_location_after (playhead_cursor->current_frame);
1835 _session->request_locate (location->start(), _session->transport_rolling());
1837 _session->request_locate (_session->current_end_frame());
1842 Editor::jump_backward_to_mark ()
1848 Location *location = _session->locations()->first_location_before (playhead_cursor->current_frame);
1851 _session->request_locate (location->start(), _session->transport_rolling());
1853 _session->goto_start ();
1860 framepos_t const pos = _session->audible_frame ();
1863 _session->locations()->next_available_name (markername, "mark");
1865 if (!choose_new_marker_name (markername)) {
1869 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1873 Editor::clear_markers ()
1876 _session->begin_reversible_command (_("clear markers"));
1877 XMLNode &before = _session->locations()->get_state();
1878 _session->locations()->clear_markers ();
1879 XMLNode &after = _session->locations()->get_state();
1880 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1881 _session->commit_reversible_command ();
1886 Editor::clear_ranges ()
1889 _session->begin_reversible_command (_("clear ranges"));
1890 XMLNode &before = _session->locations()->get_state();
1892 Location * looploc = _session->locations()->auto_loop_location();
1893 Location * punchloc = _session->locations()->auto_punch_location();
1894 Location * sessionloc = _session->locations()->session_range_location();
1896 _session->locations()->clear_ranges ();
1898 if (looploc) _session->locations()->add (looploc);
1899 if (punchloc) _session->locations()->add (punchloc);
1900 if (sessionloc) _session->locations()->add (sessionloc);
1902 XMLNode &after = _session->locations()->get_state();
1903 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1904 _session->commit_reversible_command ();
1909 Editor::clear_locations ()
1911 _session->begin_reversible_command (_("clear locations"));
1912 XMLNode &before = _session->locations()->get_state();
1913 _session->locations()->clear ();
1914 XMLNode &after = _session->locations()->get_state();
1915 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1916 _session->commit_reversible_command ();
1917 _session->locations()->clear ();
1921 Editor::unhide_markers ()
1923 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1924 Location *l = (*i).first;
1925 if (l->is_hidden() && l->is_mark()) {
1926 l->set_hidden(false, this);
1932 Editor::unhide_ranges ()
1934 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1935 Location *l = (*i).first;
1936 if (l->is_hidden() && l->is_range_marker()) {
1937 l->set_hidden(false, this);
1942 /* INSERT/REPLACE */
1945 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1950 RouteTimeAxisView *rtv = 0;
1951 boost::shared_ptr<Playlist> playlist;
1953 track_canvas->window_to_world (x, y, wx, wy);
1956 event.type = GDK_BUTTON_RELEASE;
1957 event.button.x = wx;
1958 event.button.y = wy;
1960 where = event_frame (&event, &cx, &cy);
1962 if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1963 /* clearly outside canvas area */
1967 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1968 if (tv.first == 0) {
1972 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1976 if ((playlist = rtv->playlist()) == 0) {
1982 begin_reversible_command (_("insert dragged region"));
1983 playlist->clear_changes ();
1984 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
1985 _session->add_command(new StatefulDiffCommand (playlist));
1986 commit_reversible_command ();
1990 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
1994 RouteTimeAxisView *dest_rtv = 0;
1995 RouteTimeAxisView *source_rtv = 0;
1997 track_canvas->window_to_world (x, y, wx, wy);
1998 wx += horizontal_position ();
1999 wy += vertical_adjustment.get_value();
2002 event.type = GDK_BUTTON_RELEASE;
2003 event.button.x = wx;
2004 event.button.y = wy;
2006 event_frame (&event, &cx, &cy);
2008 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2009 if (tv.first == 0) {
2013 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2017 /* use this drag source to add underlay to a track. But we really don't care
2018 about the Route, only the view of the route, so find it first */
2019 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2020 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2024 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2025 dest_rtv->add_underlay(source_rtv->view());
2032 Editor::insert_region_list_selection (float times)
2034 RouteTimeAxisView *tv = 0;
2035 boost::shared_ptr<Playlist> playlist;
2037 if (clicked_routeview != 0) {
2038 tv = clicked_routeview;
2039 } else if (!selection->tracks.empty()) {
2040 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2043 } else if (entered_track != 0) {
2044 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2051 if ((playlist = tv->playlist()) == 0) {
2055 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2060 begin_reversible_command (_("insert region"));
2061 playlist->clear_changes ();
2062 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2063 _session->add_command(new StatefulDiffCommand (playlist));
2064 commit_reversible_command ();
2067 /* BUILT-IN EFFECTS */
2070 Editor::reverse_selection ()
2075 /* GAIN ENVELOPE EDITING */
2078 Editor::edit_envelope ()
2085 Editor::transition_to_rolling (bool fwd)
2091 if (_session->config.get_external_sync()) {
2092 switch (_session->config.get_sync_source()) {
2096 /* transport controlled by the master */
2101 if (_session->is_auditioning()) {
2102 _session->cancel_audition ();
2106 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2110 Editor::play_from_start ()
2112 _session->request_locate (_session->current_start_frame(), true);
2116 Editor::play_from_edit_point ()
2118 _session->request_locate (get_preferred_edit_position(), true);
2122 Editor::play_from_edit_point_and_return ()
2124 framepos_t start_frame;
2125 framepos_t return_frame;
2127 start_frame = get_preferred_edit_position (true);
2129 if (_session->transport_rolling()) {
2130 _session->request_locate (start_frame, false);
2134 /* don't reset the return frame if its already set */
2136 if ((return_frame = _session->requested_return_frame()) < 0) {
2137 return_frame = _session->audible_frame();
2140 if (start_frame >= 0) {
2141 _session->request_roll_at_and_return (start_frame, return_frame);
2146 Editor::play_selection ()
2148 if (selection->time.empty()) {
2152 _session->request_play_range (&selection->time, true);
2156 Editor::play_location (Location& location)
2158 if (location.start() <= location.end()) {
2162 _session->request_bounded_roll (location.start(), location.end());
2166 Editor::loop_location (Location& location)
2168 if (location.start() <= location.end()) {
2174 if ((tll = transport_loop_location()) != 0) {
2175 tll->set (location.start(), location.end());
2177 // enable looping, reposition and start rolling
2178 _session->request_play_loop (true);
2179 _session->request_locate (tll->start(), true);
2184 Editor::do_layer_operation (LayerOperation op)
2186 if (selection->regions.empty ()) {
2190 bool const multiple = selection->regions.size() > 1;
2194 begin_reversible_command (_("raise regions"));
2196 begin_reversible_command (_("raise region"));
2202 begin_reversible_command (_("raise regions to top"));
2204 begin_reversible_command (_("raise region to top"));
2210 begin_reversible_command (_("lower regions"));
2212 begin_reversible_command (_("lower region"));
2218 begin_reversible_command (_("lower regions to bottom"));
2220 begin_reversible_command (_("lower region"));
2225 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2226 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2227 (*i)->clear_owned_changes ();
2230 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2231 boost::shared_ptr<Region> r = (*i)->region ();
2243 r->lower_to_bottom ();
2247 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2248 vector<Command*> cmds;
2250 _session->add_commands (cmds);
2253 commit_reversible_command ();
2257 Editor::raise_region ()
2259 do_layer_operation (Raise);
2263 Editor::raise_region_to_top ()
2265 do_layer_operation (RaiseToTop);
2269 Editor::lower_region ()
2271 do_layer_operation (Lower);
2275 Editor::lower_region_to_bottom ()
2277 do_layer_operation (LowerToBottom);
2280 /** Show the region editor for the selected regions */
2282 Editor::show_region_properties ()
2284 selection->foreach_regionview (&RegionView::show_region_editor);
2287 /** Show the midi list editor for the selected MIDI regions */
2289 Editor::show_midi_list_editor ()
2291 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2295 Editor::rename_region ()
2297 RegionSelection rs = get_regions_from_selection_and_entered ();
2303 ArdourDialog d (*this, _("Rename Region"), true, false);
2305 Label label (_("New name:"));
2308 hbox.set_spacing (6);
2309 hbox.pack_start (label, false, false);
2310 hbox.pack_start (entry, true, true);
2312 d.get_vbox()->set_border_width (12);
2313 d.get_vbox()->pack_start (hbox, false, false);
2315 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2316 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2318 d.set_size_request (300, -1);
2319 d.set_position (Gtk::WIN_POS_MOUSE);
2321 entry.set_text (rs.front()->region()->name());
2322 entry.select_region (0, -1);
2324 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2330 int const ret = d.run();
2334 if (ret != RESPONSE_OK) {
2338 std::string str = entry.get_text();
2339 strip_whitespace_edges (str);
2341 rs.front()->region()->set_name (str);
2342 _regions->redisplay ();
2347 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2349 if (_session->is_auditioning()) {
2350 _session->cancel_audition ();
2353 // note: some potential for creativity here, because region doesn't
2354 // have to belong to the playlist that Route is handling
2356 // bool was_soloed = route.soloed();
2358 route.set_solo (true, this);
2360 _session->request_bounded_roll (region->position(), region->position() + region->length());
2362 /* XXX how to unset the solo state ? */
2365 /** Start an audition of the first selected region */
2367 Editor::play_edit_range ()
2369 framepos_t start, end;
2371 if (get_edit_op_range (start, end)) {
2372 _session->request_bounded_roll (start, end);
2377 Editor::play_selected_region ()
2379 framepos_t start = max_framepos;
2382 RegionSelection rs = get_regions_from_selection_and_entered ();
2388 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2389 if ((*i)->region()->position() < start) {
2390 start = (*i)->region()->position();
2392 if ((*i)->region()->last_frame() + 1 > end) {
2393 end = (*i)->region()->last_frame() + 1;
2397 _session->request_bounded_roll (start, end);
2401 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2403 _session->audition_region (region);
2407 Editor::region_from_selection ()
2409 if (clicked_axisview == 0) {
2413 if (selection->time.empty()) {
2417 framepos_t start = selection->time[clicked_selection].start;
2418 framepos_t end = selection->time[clicked_selection].end;
2420 TrackViewList tracks = get_tracks_for_range_action ();
2422 framepos_t selection_cnt = end - start + 1;
2424 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2425 boost::shared_ptr<Region> current;
2426 boost::shared_ptr<Playlist> pl;
2427 framepos_t internal_start;
2430 if ((pl = (*i)->playlist()) == 0) {
2434 if ((current = pl->top_region_at (start)) == 0) {
2438 internal_start = start - current->position();
2439 RegionFactory::region_name (new_name, current->name(), true);
2443 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2444 plist.add (ARDOUR::Properties::length, selection_cnt);
2445 plist.add (ARDOUR::Properties::name, new_name);
2446 plist.add (ARDOUR::Properties::layer, 0);
2448 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2453 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2455 if (selection->time.empty() || selection->tracks.empty()) {
2459 framepos_t start = selection->time[clicked_selection].start;
2460 framepos_t end = selection->time[clicked_selection].end;
2462 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2463 sort_track_selection (ts);
2465 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2466 boost::shared_ptr<Region> current;
2467 boost::shared_ptr<Playlist> playlist;
2468 framepos_t internal_start;
2471 if ((playlist = (*i)->playlist()) == 0) {
2475 if ((current = playlist->top_region_at(start)) == 0) {
2479 internal_start = start - current->position();
2480 RegionFactory::region_name (new_name, current->name(), true);
2484 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2485 plist.add (ARDOUR::Properties::length, end - start + 1);
2486 plist.add (ARDOUR::Properties::name, new_name);
2488 new_regions.push_back (RegionFactory::create (current, plist));
2493 Editor::split_multichannel_region ()
2495 RegionSelection rs = get_regions_from_selection_and_entered ();
2501 vector< boost::shared_ptr<Region> > v;
2503 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2504 (*x)->region()->separate_by_channel (*_session, v);
2509 Editor::new_region_from_selection ()
2511 region_from_selection ();
2512 cancel_selection ();
2516 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2518 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2519 case Evoral::OverlapNone:
2527 * - selected tracks, or if there are none...
2528 * - tracks containing selected regions, or if there are none...
2533 Editor::get_tracks_for_range_action () const
2537 if (selection->tracks.empty()) {
2539 /* use tracks with selected regions */
2541 RegionSelection rs = selection->regions;
2543 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2544 TimeAxisView* tv = &(*i)->get_time_axis_view();
2546 if (!t.contains (tv)) {
2552 /* no regions and no tracks: use all tracks */
2558 t = selection->tracks;
2561 return t.filter_to_unique_playlists();
2565 Editor::separate_regions_between (const TimeSelection& ts)
2567 bool in_command = false;
2568 boost::shared_ptr<Playlist> playlist;
2569 RegionSelection new_selection;
2571 TrackViewList tmptracks = get_tracks_for_range_action ();
2572 sort_track_selection (tmptracks);
2574 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2576 RouteTimeAxisView* rtv;
2578 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2580 if (rtv->is_track()) {
2582 /* no edits to destructive tracks */
2584 if (rtv->track()->destructive()) {
2588 if ((playlist = rtv->playlist()) != 0) {
2590 playlist->clear_changes ();
2592 /* XXX need to consider musical time selections here at some point */
2594 double speed = rtv->track()->speed();
2597 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2599 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2600 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2602 latest_regionviews.clear ();
2604 playlist->partition ((framepos_t)((*t).start * speed),
2605 (framepos_t)((*t).end * speed), false);
2609 if (!latest_regionviews.empty()) {
2611 rtv->view()->foreach_regionview (sigc::bind (
2612 sigc::ptr_fun (add_if_covered),
2613 &(*t), &new_selection));
2616 begin_reversible_command (_("separate"));
2620 /* pick up changes to existing regions */
2622 vector<Command*> cmds;
2623 playlist->rdiff (cmds);
2624 _session->add_commands (cmds);
2626 /* pick up changes to the playlist itself (adds/removes)
2629 _session->add_command(new StatefulDiffCommand (playlist));
2638 selection->set (new_selection);
2639 set_mouse_mode (MouseObject);
2641 commit_reversible_command ();
2645 struct PlaylistState {
2646 boost::shared_ptr<Playlist> playlist;
2650 /** Take tracks from get_tracks_for_range_action and cut any regions
2651 * on those tracks so that the tracks are empty over the time
2655 Editor::separate_region_from_selection ()
2657 /* preferentially use *all* ranges in the time selection if we're in range mode
2658 to allow discontiguous operation, since get_edit_op_range() currently
2659 returns a single range.
2662 if (mouse_mode == MouseRange && !selection->time.empty()) {
2664 separate_regions_between (selection->time);
2671 if (get_edit_op_range (start, end)) {
2673 AudioRange ar (start, end, 1);
2677 separate_regions_between (ts);
2683 Editor::separate_region_from_punch ()
2685 Location* loc = _session->locations()->auto_punch_location();
2687 separate_regions_using_location (*loc);
2692 Editor::separate_region_from_loop ()
2694 Location* loc = _session->locations()->auto_loop_location();
2696 separate_regions_using_location (*loc);
2701 Editor::separate_regions_using_location (Location& loc)
2703 if (loc.is_mark()) {
2707 AudioRange ar (loc.start(), loc.end(), 1);
2712 separate_regions_between (ts);
2715 /** Separate regions under the selected region */
2717 Editor::separate_under_selected_regions ()
2719 vector<PlaylistState> playlists;
2723 rs = get_regions_from_selection_and_entered();
2725 if (!_session || rs.empty()) {
2729 begin_reversible_command (_("separate region under"));
2731 list<boost::shared_ptr<Region> > regions_to_remove;
2733 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2734 // we can't just remove the region(s) in this loop because
2735 // this removes them from the RegionSelection, and they thus
2736 // disappear from underneath the iterator, and the ++i above
2737 // SEGVs in a puzzling fashion.
2739 // so, first iterate over the regions to be removed from rs and
2740 // add them to the regions_to_remove list, and then
2741 // iterate over the list to actually remove them.
2743 regions_to_remove.push_back ((*i)->region());
2746 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2748 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2751 // is this check necessary?
2755 vector<PlaylistState>::iterator i;
2757 //only take state if this is a new playlist.
2758 for (i = playlists.begin(); i != playlists.end(); ++i) {
2759 if ((*i).playlist == playlist) {
2764 if (i == playlists.end()) {
2766 PlaylistState before;
2767 before.playlist = playlist;
2768 before.before = &playlist->get_state();
2770 playlist->freeze ();
2771 playlists.push_back(before);
2774 //Partition on the region bounds
2775 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2777 //Re-add region that was just removed due to the partition operation
2778 playlist->add_region( (*rl), (*rl)->first_frame() );
2781 vector<PlaylistState>::iterator pl;
2783 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2784 (*pl).playlist->thaw ();
2785 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2788 commit_reversible_command ();
2792 Editor::crop_region_to_selection ()
2794 if (!selection->time.empty()) {
2796 crop_region_to (selection->time.start(), selection->time.end_frame());
2803 if (get_edit_op_range (start, end)) {
2804 crop_region_to (start, end);
2811 Editor::crop_region_to (framepos_t start, framepos_t end)
2813 vector<boost::shared_ptr<Playlist> > playlists;
2814 boost::shared_ptr<Playlist> playlist;
2817 if (selection->tracks.empty()) {
2818 ts = track_views.filter_to_unique_playlists();
2820 ts = selection->tracks.filter_to_unique_playlists ();
2823 sort_track_selection (ts);
2825 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2827 RouteTimeAxisView* rtv;
2829 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2831 boost::shared_ptr<Track> t = rtv->track();
2833 if (t != 0 && ! t->destructive()) {
2835 if ((playlist = rtv->playlist()) != 0) {
2836 playlists.push_back (playlist);
2842 if (playlists.empty()) {
2846 framepos_t the_start;
2850 begin_reversible_command (_("trim to selection"));
2852 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2854 boost::shared_ptr<Region> region;
2858 if ((region = (*i)->top_region_at(the_start)) == 0) {
2862 /* now adjust lengths to that we do the right thing
2863 if the selection extends beyond the region
2866 the_start = max (the_start, (framepos_t) region->position());
2867 if (max_framepos - the_start < region->length()) {
2868 the_end = the_start + region->length() - 1;
2870 the_end = max_framepos;
2872 the_end = min (end, the_end);
2873 cnt = the_end - the_start + 1;
2875 region->clear_changes ();
2876 region->trim_to (the_start, cnt);
2877 _session->add_command (new StatefulDiffCommand (region));
2880 commit_reversible_command ();
2884 Editor::region_fill_track ()
2886 RegionSelection rs = get_regions_from_selection_and_entered ();
2888 if (!_session || rs.empty()) {
2892 framepos_t const end = _session->current_end_frame ();
2894 begin_reversible_command (Operations::region_fill);
2896 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2898 boost::shared_ptr<Region> region ((*i)->region());
2900 boost::shared_ptr<Playlist> pl = region->playlist();
2902 if (end <= region->last_frame()) {
2906 double times = (double) (end - region->last_frame()) / (double) region->length();
2912 pl->clear_changes ();
2913 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2914 _session->add_command (new StatefulDiffCommand (pl));
2917 commit_reversible_command ();
2921 Editor::region_fill_selection ()
2923 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2927 if (selection->time.empty()) {
2931 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2936 framepos_t start = selection->time[clicked_selection].start;
2937 framepos_t end = selection->time[clicked_selection].end;
2939 boost::shared_ptr<Playlist> playlist;
2941 if (selection->tracks.empty()) {
2945 framepos_t selection_length = end - start;
2946 float times = (float)selection_length / region->length();
2948 begin_reversible_command (Operations::fill_selection);
2950 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2952 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
2954 if ((playlist = (*i)->playlist()) == 0) {
2958 playlist->clear_changes ();
2959 playlist->add_region (RegionFactory::create (region, true), start, times);
2960 _session->add_command (new StatefulDiffCommand (playlist));
2963 commit_reversible_command ();
2967 Editor::set_region_sync_position ()
2969 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
2973 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
2975 bool in_command = false;
2977 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2979 if (!(*r)->region()->covers (where)) {
2983 boost::shared_ptr<Region> region ((*r)->region());
2986 begin_reversible_command (_("set sync point"));
2990 region->clear_changes ();
2991 region->set_sync_position (where);
2992 _session->add_command(new StatefulDiffCommand (region));
2996 commit_reversible_command ();
3000 /** Remove the sync positions of the selection */
3002 Editor::remove_region_sync ()
3004 RegionSelection rs = get_regions_from_selection_and_entered ();
3010 begin_reversible_command (_("remove region sync"));
3012 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3014 (*i)->region()->clear_changes ();
3015 (*i)->region()->clear_sync_position ();
3016 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3019 commit_reversible_command ();
3023 Editor::naturalize_region ()
3025 RegionSelection rs = get_regions_from_selection_and_entered ();
3031 if (rs.size() > 1) {
3032 begin_reversible_command (_("move regions to original position"));
3034 begin_reversible_command (_("move region to original position"));
3037 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3038 (*i)->region()->clear_changes ();
3039 (*i)->region()->move_to_natural_position ();
3040 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3043 commit_reversible_command ();
3047 Editor::align_regions (RegionPoint what)
3049 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3055 begin_reversible_command (_("align selection"));
3057 framepos_t const position = get_preferred_edit_position ();
3059 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3060 align_region_internal ((*i)->region(), what, position);
3063 commit_reversible_command ();
3066 struct RegionSortByTime {
3067 bool operator() (const RegionView* a, const RegionView* b) {
3068 return a->region()->position() < b->region()->position();
3073 Editor::align_regions_relative (RegionPoint point)
3075 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3081 framepos_t const position = get_preferred_edit_position ();
3083 framepos_t distance = 0;
3087 list<RegionView*> sorted;
3088 rs.by_position (sorted);
3090 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3095 if (position > r->position()) {
3096 distance = position - r->position();
3098 distance = r->position() - position;
3104 if (position > r->last_frame()) {
3105 distance = position - r->last_frame();
3106 pos = r->position() + distance;
3108 distance = r->last_frame() - position;
3109 pos = r->position() - distance;
3115 pos = r->adjust_to_sync (position);
3116 if (pos > r->position()) {
3117 distance = pos - r->position();
3119 distance = r->position() - pos;
3125 if (pos == r->position()) {
3129 begin_reversible_command (_("align selection (relative)"));
3131 /* move first one specially */
3133 r->clear_changes ();
3134 r->set_position (pos);
3135 _session->add_command(new StatefulDiffCommand (r));
3137 /* move rest by the same amount */
3141 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3143 boost::shared_ptr<Region> region ((*i)->region());
3145 region->clear_changes ();
3148 region->set_position (region->position() + distance);
3150 region->set_position (region->position() - distance);
3153 _session->add_command(new StatefulDiffCommand (region));
3157 commit_reversible_command ();
3161 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3163 begin_reversible_command (_("align region"));
3164 align_region_internal (region, point, position);
3165 commit_reversible_command ();
3169 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3171 region->clear_changes ();
3175 region->set_position (region->adjust_to_sync (position));
3179 if (position > region->length()) {
3180 region->set_position (position - region->length());
3185 region->set_position (position);
3189 _session->add_command(new StatefulDiffCommand (region));
3193 Editor::trim_region_front ()
3199 Editor::trim_region_back ()
3201 trim_region (false);
3205 Editor::trim_region (bool front)
3207 framepos_t where = get_preferred_edit_position();
3208 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3214 begin_reversible_command (front ? _("trim front") : _("trim back"));
3216 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3217 if (!(*i)->region()->locked()) {
3219 (*i)->region()->clear_changes ();
3222 (*i)->region()->trim_front (where);
3224 (*i)->region()->trim_end (where);
3227 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3231 commit_reversible_command ();
3234 /** Trim the end of the selected regions to the position of the edit cursor */
3236 Editor::trim_region_to_loop ()
3238 Location* loc = _session->locations()->auto_loop_location();
3242 trim_region_to_location (*loc, _("trim to loop"));
3246 Editor::trim_region_to_punch ()
3248 Location* loc = _session->locations()->auto_punch_location();
3252 trim_region_to_location (*loc, _("trim to punch"));
3256 Editor::trim_region_to_location (const Location& loc, const char* str)
3258 RegionSelection rs = get_regions_from_selection_and_entered ();
3260 begin_reversible_command (str);
3262 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3263 RegionView* rv = (*x);
3265 /* require region to span proposed trim */
3266 switch (rv->region()->coverage (loc.start(), loc.end())) {
3267 case Evoral::OverlapInternal:
3273 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3282 if (tav->track() != 0) {
3283 speed = tav->track()->speed();
3286 start = session_frame_to_track_frame (loc.start(), speed);
3287 end = session_frame_to_track_frame (loc.end(), speed);
3289 rv->region()->clear_changes ();
3290 rv->region()->trim_to (start, (end - start));
3291 _session->add_command(new StatefulDiffCommand (rv->region()));
3294 commit_reversible_command ();
3298 Editor::trim_region_to_previous_region_end ()
3300 return trim_to_region(false);
3304 Editor::trim_region_to_next_region_start ()
3306 return trim_to_region(true);
3310 Editor::trim_to_region(bool forward)
3312 RegionSelection rs = get_regions_from_selection_and_entered ();
3314 begin_reversible_command (_("trim to region"));
3316 boost::shared_ptr<Region> next_region;
3318 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3320 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3326 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3334 if (atav->track() != 0) {
3335 speed = atav->track()->speed();
3339 boost::shared_ptr<Region> region = arv->region();
3340 boost::shared_ptr<Playlist> playlist (region->playlist());
3342 region->clear_changes ();
3346 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3352 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3353 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3357 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3363 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3365 arv->region_changed (ARDOUR::bounds_change);
3368 _session->add_command(new StatefulDiffCommand (region));
3371 commit_reversible_command ();
3375 Editor::unfreeze_route ()
3377 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3381 clicked_routeview->track()->unfreeze ();
3385 Editor::_freeze_thread (void* arg)
3387 return static_cast<Editor*>(arg)->freeze_thread ();
3391 Editor::freeze_thread ()
3393 /* create event pool because we may need to talk to the session */
3394 SessionEvent::create_per_thread_pool ("freeze events", 64);
3395 /* create per-thread buffers for process() tree to use */
3396 current_interthread_info->process_thread.init ();
3397 current_interthread_info->process_thread.get_buffers ();
3398 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3399 current_interthread_info->done = true;
3400 current_interthread_info->process_thread.drop_buffers();
3405 Editor::freeze_route ()
3411 /* stop transport before we start. this is important */
3413 _session->request_transport_speed (0.0);
3415 /* wait for just a little while, because the above call is asynchronous */
3419 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3423 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3425 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3426 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3428 d.set_title (_("Cannot freeze"));
3433 if (clicked_routeview->track()->has_external_redirects()) {
3434 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"
3435 "Freezing will only process the signal as far as the first send/insert/return."),
3436 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3438 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3439 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3440 d.set_title (_("Freeze Limits"));
3442 int response = d.run ();
3445 case Gtk::RESPONSE_CANCEL:
3452 InterThreadInfo itt;
3453 current_interthread_info = &itt;
3455 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3457 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3459 set_canvas_cursor (_cursors->wait);
3461 while (!itt.done && !itt.cancel) {
3462 gtk_main_iteration ();
3465 current_interthread_info = 0;
3466 set_canvas_cursor (current_canvas_cursor);
3470 Editor::bounce_range_selection (bool replace, bool enable_processing)
3472 if (selection->time.empty()) {
3476 TrackSelection views = selection->tracks;
3478 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3480 if (enable_processing) {
3482 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3484 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3486 _("You can't perform this operation because the processing of the signal "
3487 "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3488 "You can do this without processing, which is a different operation.")
3490 d.set_title (_("Cannot bounce"));
3497 framepos_t start = selection->time[clicked_selection].start;
3498 framepos_t end = selection->time[clicked_selection].end;
3499 framepos_t cnt = end - start + 1;
3501 begin_reversible_command (_("bounce range"));
3503 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3505 RouteTimeAxisView* rtv;
3507 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3511 boost::shared_ptr<Playlist> playlist;
3513 if ((playlist = rtv->playlist()) == 0) {
3517 InterThreadInfo itt;
3519 playlist->clear_changes ();
3520 playlist->clear_owned_changes ();
3522 boost::shared_ptr<Region> r;
3524 if (enable_processing) {
3525 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3527 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3535 list<AudioRange> ranges;
3536 ranges.push_back (AudioRange (start, start+cnt, 0));
3537 playlist->cut (ranges); // discard result
3538 playlist->add_region (r, start);
3541 vector<Command*> cmds;
3542 playlist->rdiff (cmds);
3543 _session->add_commands (cmds);
3545 _session->add_command (new StatefulDiffCommand (playlist));
3548 commit_reversible_command ();
3551 /** Delete selected regions, automation points or a time range */
3558 /** Cut selected regions, automation points or a time range */
3565 /** Copy selected regions, automation points or a time range */
3573 /** @return true if a Cut, Copy or Clear is possible */
3575 Editor::can_cut_copy () const
3577 switch (current_mouse_mode()) {
3580 if (!selection->regions.empty() || !selection->points.empty()) {
3586 if (!selection->time.empty()) {
3599 /** Cut, copy or clear selected regions, automation points or a time range.
3600 * @param op Operation (Cut, Copy or Clear)
3603 Editor::cut_copy (CutCopyOp op)
3605 /* only cancel selection if cut/copy is successful.*/
3611 opname = _("delete");
3620 opname = _("clear");
3624 /* if we're deleting something, and the mouse is still pressed,
3625 the thing we started a drag for will be gone when we release
3626 the mouse button(s). avoid this. see part 2 at the end of
3630 if (op == Delete || op == Cut || op == Clear) {
3631 if (_drags->active ()) {
3636 cut_buffer->clear ();
3638 if (entered_marker) {
3640 /* cut/delete op while pointing at a marker */
3643 Location* loc = find_location_from_marker (entered_marker, ignored);
3645 if (_session && loc) {
3646 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3653 if (internal_editing()) {
3655 switch (current_mouse_mode()) {
3668 /* we only want to cut regions if some are selected */
3670 if (doing_object_stuff()) {
3671 rs = get_regions_from_selection ();
3672 if (!rs.empty() || !selection->points.empty()) {
3674 begin_reversible_command (opname + _(" objects"));
3677 cut_copy_regions (op, rs);
3679 if (op == Cut || op == Delete) {
3680 selection->clear_regions ();
3684 if (!selection->points.empty()) {
3685 cut_copy_points (op);
3687 if (op == Cut || op == Delete) {
3688 selection->clear_points ();
3691 commit_reversible_command ();
3694 if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3695 /* don't cause suprises */
3700 if (doing_range_stuff()) {
3701 if (selection->time.empty()) {
3702 framepos_t start, end;
3703 if (!get_edit_op_range (start, end)) {
3706 selection->set (start, end);
3709 begin_reversible_command (opname + _(" range"));
3710 cut_copy_ranges (op);
3711 commit_reversible_command ();
3713 if (op == Cut || op == Delete) {
3714 selection->clear_time ();
3720 if (op == Delete || op == Cut || op == Clear) {
3725 struct AutomationRecord {
3726 AutomationRecord () : state (0) {}
3727 AutomationRecord (XMLNode* s) : state (s) {}
3729 XMLNode* state; ///< state before any operation
3730 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3733 /** Cut, copy or clear selected automation points.
3734 * @param op Operation (Cut, Copy or Clear)
3737 Editor::cut_copy_points (CutCopyOp op)
3739 if (selection->points.empty ()) {
3743 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3744 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3746 /* Keep a record of the AutomationLists that we end up using in this operation */
3747 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3750 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3751 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3752 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3753 if (lists.find (al) == lists.end ()) {
3754 /* We haven't seen this list yet, so make a record for it. This includes
3755 taking a copy of its current state, in case this is needed for undo later.
3757 lists[al] = AutomationRecord (&al->get_state ());
3761 if (op == Cut || op == Copy) {
3762 /* This operation will involve putting things in the cut buffer, so create an empty
3763 ControlList for each of our source lists to put the cut buffer data in.
3765 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3766 i->second.copy = i->first->create (i->first->parameter ());
3769 /* Add all selected points to the relevant copy ControlLists */
3770 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3771 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3772 AutomationList::const_iterator j = (*i)->model ();
3773 lists[al].copy->add ((*j)->when, (*j)->value);
3776 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3777 /* Correct this copy list so that it starts at time 0 */
3778 double const start = i->second.copy->front()->when;
3779 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3780 (*j)->when -= start;
3783 /* And add it to the cut buffer */
3784 cut_buffer->add (i->second.copy);
3788 if (op == Delete || op == Cut) {
3789 /* This operation needs to remove things from the main AutomationList, so do that now */
3791 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3792 i->first->freeze ();
3795 /* Remove each selected point from its AutomationList */
3796 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3797 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3798 al->erase ((*i)->model ());
3801 /* Thaw the lists and add undo records for them */
3802 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3803 boost::shared_ptr<AutomationList> al = i->first;
3805 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3810 /** Cut, copy or clear selected automation points.
3811 * @param op Operation (Cut, Copy or Clear)
3814 Editor::cut_copy_midi (CutCopyOp op)
3816 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3817 MidiRegionView* mrv = *i;
3818 mrv->cut_copy_clear (op);
3824 struct lt_playlist {
3825 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3826 return a.playlist < b.playlist;
3830 struct PlaylistMapping {
3832 boost::shared_ptr<Playlist> pl;
3834 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3837 /** Remove `clicked_regionview' */
3839 Editor::remove_clicked_region ()
3841 if (clicked_routeview == 0 || clicked_regionview == 0) {
3845 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3847 begin_reversible_command (_("remove region"));
3848 playlist->clear_changes ();
3849 playlist->clear_owned_changes ();
3850 playlist->remove_region (clicked_regionview->region());
3852 /* We might have removed regions, which alters other regions' layering_index,
3853 so we need to do a recursive diff here.
3855 vector<Command*> cmds;
3856 playlist->rdiff (cmds);
3857 _session->add_commands (cmds);
3859 _session->add_command(new StatefulDiffCommand (playlist));
3860 commit_reversible_command ();
3864 /** Remove the selected regions */
3866 Editor::remove_selected_regions ()
3868 RegionSelection rs = get_regions_from_selection_and_entered ();
3870 if (!_session || rs.empty()) {
3874 begin_reversible_command (_("remove region"));
3876 list<boost::shared_ptr<Region> > regions_to_remove;
3878 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3879 // we can't just remove the region(s) in this loop because
3880 // this removes them from the RegionSelection, and they thus
3881 // disappear from underneath the iterator, and the ++i above
3882 // SEGVs in a puzzling fashion.
3884 // so, first iterate over the regions to be removed from rs and
3885 // add them to the regions_to_remove list, and then
3886 // iterate over the list to actually remove them.
3888 regions_to_remove.push_back ((*i)->region());
3891 vector<boost::shared_ptr<Playlist> > playlists;
3893 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3895 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3898 // is this check necessary?
3902 /* get_regions_from_selection_and_entered() guarantees that
3903 the playlists involved are unique, so there is no need
3907 playlists.push_back (playlist);
3909 playlist->clear_changes ();
3910 playlist->clear_owned_changes ();
3911 playlist->freeze ();
3912 playlist->remove_region (*rl);
3915 vector<boost::shared_ptr<Playlist> >::iterator pl;
3917 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3920 /* We might have removed regions, which alters other regions' layering_index,
3921 so we need to do a recursive diff here.
3923 vector<Command*> cmds;
3924 (*pl)->rdiff (cmds);
3925 _session->add_commands (cmds);
3927 _session->add_command(new StatefulDiffCommand (*pl));
3930 commit_reversible_command ();
3933 /** Cut, copy or clear selected regions.
3934 * @param op Operation (Cut, Copy or Clear)
3937 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3939 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3940 a map when we want ordered access to both elements. i think.
3943 vector<PlaylistMapping> pmap;
3945 framepos_t first_position = max_framepos;
3947 typedef set<boost::shared_ptr<Playlist> > FreezeList;
3948 FreezeList freezelist;
3950 /* get ordering correct before we cut/copy */
3952 rs.sort_by_position_and_track ();
3954 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3956 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3958 if (op == Cut || op == Clear || op == Delete) {
3959 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3962 FreezeList::iterator fl;
3964 // only take state if this is a new playlist.
3965 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3971 if (fl == freezelist.end()) {
3972 pl->clear_changes();
3973 pl->clear_owned_changes ();
3975 freezelist.insert (pl);
3980 TimeAxisView* tv = &(*x)->get_time_axis_view();
3981 vector<PlaylistMapping>::iterator z;
3983 for (z = pmap.begin(); z != pmap.end(); ++z) {
3984 if ((*z).tv == tv) {
3989 if (z == pmap.end()) {
3990 pmap.push_back (PlaylistMapping (tv));
3994 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3996 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3999 /* region not yet associated with a playlist (e.g. unfinished
4006 TimeAxisView& tv = (*x)->get_time_axis_view();
4007 boost::shared_ptr<Playlist> npl;
4008 RegionSelection::iterator tmp;
4015 vector<PlaylistMapping>::iterator z;
4017 for (z = pmap.begin(); z != pmap.end(); ++z) {
4018 if ((*z).tv == &tv) {
4023 assert (z != pmap.end());
4026 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4034 boost::shared_ptr<Region> r = (*x)->region();
4035 boost::shared_ptr<Region> _xx;
4041 pl->remove_region (r);
4045 _xx = RegionFactory::create (r);
4046 npl->add_region (_xx, r->position() - first_position);
4047 pl->remove_region (r);
4051 /* copy region before adding, so we're not putting same object into two different playlists */
4052 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4056 pl->remove_region (r);
4065 list<boost::shared_ptr<Playlist> > foo;
4067 /* the pmap is in the same order as the tracks in which selected regions occured */
4069 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4072 foo.push_back ((*i).pl);
4077 cut_buffer->set (foo);
4081 _last_cut_copy_source_track = 0;
4083 _last_cut_copy_source_track = pmap.front().tv;
4087 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4090 /* We might have removed regions, which alters other regions' layering_index,
4091 so we need to do a recursive diff here.
4093 vector<Command*> cmds;
4094 (*pl)->rdiff (cmds);
4095 _session->add_commands (cmds);
4097 _session->add_command (new StatefulDiffCommand (*pl));
4102 Editor::cut_copy_ranges (CutCopyOp op)
4104 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4106 /* Sort the track selection now, so that it if is used, the playlists
4107 selected by the calls below to cut_copy_clear are in the order that
4108 their tracks appear in the editor. This makes things like paste
4109 of ranges work properly.
4112 sort_track_selection (ts);
4115 if (!entered_track) {
4118 ts.push_back (entered_track);
4121 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4122 (*i)->cut_copy_clear (*selection, op);
4127 Editor::paste (float times, bool from_context)
4129 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4131 paste_internal (get_preferred_edit_position (false, from_context), times);
4135 Editor::mouse_paste ()
4140 if (!mouse_frame (where, ignored)) {
4145 paste_internal (where, 1);
4149 Editor::paste_internal (framepos_t position, float times)
4151 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4153 if (internal_editing()) {
4154 if (cut_buffer->midi_notes.empty()) {
4158 if (cut_buffer->empty()) {
4163 if (position == max_framepos) {
4164 position = get_preferred_edit_position();
4165 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4169 TrackViewList::iterator i;
4172 /* get everything in the correct order */
4174 if (!selection->tracks.empty()) {
4175 /* there are some selected tracks, so paste to them */
4176 ts = selection->tracks.filter_to_unique_playlists ();
4177 sort_track_selection (ts);
4178 } else if (_last_cut_copy_source_track) {
4179 /* otherwise paste to the track that the cut/copy came from;
4180 see discussion in mantis #3333.
4182 ts.push_back (_last_cut_copy_source_track);
4185 if (internal_editing ()) {
4187 /* undo/redo is handled by individual tracks/regions */
4189 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4192 RegionSelection::iterator r;
4193 MidiNoteSelection::iterator cb;
4195 get_regions_at (rs, position, ts);
4197 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4198 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4199 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4201 mrv->paste (position, times, **cb);
4209 /* we do redo (do you do voodoo?) */
4211 begin_reversible_command (Operations::paste);
4213 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4214 (*i)->paste (position, times, *cut_buffer, nth);
4217 commit_reversible_command ();
4222 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4224 boost::shared_ptr<Playlist> playlist;
4225 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4226 RegionSelection foo;
4228 framepos_t const start_frame = regions.start ();
4229 framepos_t const end_frame = regions.end_frame ();
4231 begin_reversible_command (Operations::duplicate_region);
4233 selection->clear_regions ();
4235 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4237 boost::shared_ptr<Region> r ((*i)->region());
4239 TimeAxisView& tv = (*i)->get_time_axis_view();
4240 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4241 latest_regionviews.clear ();
4242 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4244 playlist = (*i)->region()->playlist();
4245 playlist->clear_changes ();
4246 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4247 _session->add_command(new StatefulDiffCommand (playlist));
4251 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4254 commit_reversible_command ();
4257 selection->set (foo);
4262 Editor::duplicate_selection (float times)
4264 if (selection->time.empty() || selection->tracks.empty()) {
4268 boost::shared_ptr<Playlist> playlist;
4269 vector<boost::shared_ptr<Region> > new_regions;
4270 vector<boost::shared_ptr<Region> >::iterator ri;
4272 create_region_from_selection (new_regions);
4274 if (new_regions.empty()) {
4278 begin_reversible_command (_("duplicate selection"));
4280 ri = new_regions.begin();
4282 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4284 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4285 if ((playlist = (*i)->playlist()) == 0) {
4288 playlist->clear_changes ();
4289 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4290 _session->add_command (new StatefulDiffCommand (playlist));
4293 if (ri == new_regions.end()) {
4298 commit_reversible_command ();
4301 /** Reset all selected points to the relevant default value */
4303 Editor::reset_point_selection ()
4305 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4306 ARDOUR::AutomationList::iterator j = (*i)->model ();
4307 (*j)->value = (*i)->line().the_list()->default_value ();
4312 Editor::center_playhead ()
4314 float page = _canvas_width * frames_per_unit;
4315 center_screen_internal (playhead_cursor->current_frame, page);
4319 Editor::center_edit_point ()
4321 float page = _canvas_width * frames_per_unit;
4322 center_screen_internal (get_preferred_edit_position(), page);
4325 /** Caller must begin and commit a reversible command */
4327 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4329 playlist->clear_changes ();
4331 _session->add_command (new StatefulDiffCommand (playlist));
4335 Editor::nudge_track (bool use_edit, bool forwards)
4337 boost::shared_ptr<Playlist> playlist;
4338 framepos_t distance;
4339 framepos_t next_distance;
4343 start = get_preferred_edit_position();
4348 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4352 if (selection->tracks.empty()) {
4356 begin_reversible_command (_("nudge track"));
4358 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4360 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4362 if ((playlist = (*i)->playlist()) == 0) {
4366 playlist->clear_changes ();
4367 playlist->clear_owned_changes ();
4369 playlist->nudge_after (start, distance, forwards);
4371 vector<Command*> cmds;
4373 playlist->rdiff (cmds);
4374 _session->add_commands (cmds);
4376 _session->add_command (new StatefulDiffCommand (playlist));
4379 commit_reversible_command ();
4383 Editor::remove_last_capture ()
4385 vector<string> choices;
4392 if (Config->get_verify_remove_last_capture()) {
4393 prompt = _("Do you really want to destroy the last capture?"
4394 "\n(This is destructive and cannot be undone)");
4396 choices.push_back (_("No, do nothing."));
4397 choices.push_back (_("Yes, destroy it."));
4399 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4401 if (prompter.run () == 1) {
4402 _session->remove_last_capture ();
4403 _regions->redisplay ();
4407 _session->remove_last_capture();
4408 _regions->redisplay ();
4413 Editor::normalize_region ()
4419 RegionSelection rs = get_regions_from_selection_and_entered ();
4425 NormalizeDialog dialog (rs.size() > 1);
4427 if (dialog.run () == RESPONSE_CANCEL) {
4431 set_canvas_cursor (_cursors->wait);
4434 /* XXX: should really only count audio regions here */
4435 int const regions = rs.size ();
4437 /* Make a list of the selected audio regions' maximum amplitudes, and also
4438 obtain the maximum amplitude of them all.
4440 list<double> max_amps;
4442 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4443 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4445 dialog.descend (1.0 / regions);
4446 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4449 /* the user cancelled the operation */
4450 set_canvas_cursor (current_canvas_cursor);
4454 max_amps.push_back (a);
4455 max_amp = max (max_amp, a);
4460 begin_reversible_command (_("normalize"));
4462 list<double>::const_iterator a = max_amps.begin ();
4464 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4465 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4470 arv->region()->clear_changes ();
4472 double const amp = dialog.normalize_individually() ? *a : max_amp;
4474 arv->audio_region()->normalize (amp, dialog.target ());
4475 _session->add_command (new StatefulDiffCommand (arv->region()));
4480 commit_reversible_command ();
4481 set_canvas_cursor (current_canvas_cursor);
4486 Editor::reset_region_scale_amplitude ()
4492 RegionSelection rs = get_regions_from_selection_and_entered ();
4498 begin_reversible_command ("reset gain");
4500 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4501 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4504 arv->region()->clear_changes ();
4505 arv->audio_region()->set_scale_amplitude (1.0f);
4506 _session->add_command (new StatefulDiffCommand (arv->region()));
4509 commit_reversible_command ();
4513 Editor::adjust_region_gain (bool up)
4515 RegionSelection rs = get_regions_from_selection_and_entered ();
4517 if (!_session || rs.empty()) {
4521 begin_reversible_command ("adjust region gain");
4523 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4524 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4529 arv->region()->clear_changes ();
4531 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4539 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4540 _session->add_command (new StatefulDiffCommand (arv->region()));
4543 commit_reversible_command ();
4548 Editor::reverse_region ()
4554 Reverse rev (*_session);
4555 apply_filter (rev, _("reverse regions"));
4559 Editor::strip_region_silence ()
4565 RegionSelection rs = get_regions_from_selection_and_entered ();
4571 std::list<RegionView*> audio_only;
4573 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4574 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4576 audio_only.push_back (arv);
4580 StripSilenceDialog d (_session, audio_only);
4581 int const r = d.run ();
4585 if (r == Gtk::RESPONSE_OK) {
4586 ARDOUR::AudioIntervalMap silences;
4587 d.silences (silences);
4588 StripSilence s (*_session, silences, d.fade_length());
4589 apply_filter (s, _("strip silence"), &d);
4594 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4596 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4597 mrv.selection_as_notelist (selected, true);
4599 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4600 v.push_back (selected);
4602 framepos_t pos_frames = mrv.midi_region()->position();
4603 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4605 return op (mrv.midi_region()->model(), pos_beats, v);
4609 Editor::apply_midi_note_edit_op (MidiOperator& op)
4613 RegionSelection rs = get_regions_from_selection_and_entered ();
4619 begin_reversible_command (op.name ());
4621 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4622 RegionSelection::iterator tmp = r;
4625 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4628 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4631 _session->add_command (cmd);
4638 commit_reversible_command ();
4642 Editor::fork_region ()
4644 RegionSelection rs = get_regions_from_selection_and_entered ();
4650 begin_reversible_command (_("Fork Region(s)"));
4652 set_canvas_cursor (_cursors->wait);
4655 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4656 RegionSelection::iterator tmp = r;
4659 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4662 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4663 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4665 playlist->clear_changes ();
4666 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4667 _session->add_command(new StatefulDiffCommand (playlist));
4673 commit_reversible_command ();
4675 set_canvas_cursor (current_canvas_cursor);
4679 Editor::quantize_region ()
4681 int selected_midi_region_cnt = 0;
4687 RegionSelection rs = get_regions_from_selection_and_entered ();
4693 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4694 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4696 selected_midi_region_cnt++;
4700 if (selected_midi_region_cnt == 0) {
4704 QuantizeDialog* qd = new QuantizeDialog (*this);
4707 const int r = qd->run ();
4710 if (r == Gtk::RESPONSE_OK) {
4711 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4712 qd->start_grid_size(), qd->end_grid_size(),
4713 qd->strength(), qd->swing(), qd->threshold());
4715 apply_midi_note_edit_op (quant);
4720 Editor::insert_patch_change (bool from_context)
4722 RegionSelection rs = get_regions_from_selection_and_entered ();
4728 const framepos_t p = get_preferred_edit_position (false, from_context);
4730 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4731 there may be more than one, but the PatchChangeDialog can only offer
4732 one set of patch menus.
4734 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4736 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4737 PatchChangeDialog d (0, _session, empty, first->model_name(), first->custom_device_mode(), Gtk::Stock::ADD);
4739 if (d.run() == RESPONSE_CANCEL) {
4743 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4744 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4746 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4747 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4754 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4756 RegionSelection rs = get_regions_from_selection_and_entered ();
4762 begin_reversible_command (command);
4764 set_canvas_cursor (_cursors->wait);
4768 int const N = rs.size ();
4770 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4771 RegionSelection::iterator tmp = r;
4774 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4776 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4779 progress->descend (1.0 / N);
4782 if (arv->audio_region()->apply (filter, progress) == 0) {
4784 playlist->clear_changes ();
4785 playlist->clear_owned_changes ();
4787 if (filter.results.empty ()) {
4789 /* no regions returned; remove the old one */
4790 playlist->remove_region (arv->region ());
4794 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4796 /* first region replaces the old one */
4797 playlist->replace_region (arv->region(), *res, (*res)->position());
4801 while (res != filter.results.end()) {
4802 playlist->add_region (*res, (*res)->position());
4808 /* We might have removed regions, which alters other regions' layering_index,
4809 so we need to do a recursive diff here.
4811 vector<Command*> cmds;
4812 playlist->rdiff (cmds);
4813 _session->add_commands (cmds);
4815 _session->add_command(new StatefulDiffCommand (playlist));
4821 progress->ascend ();
4829 commit_reversible_command ();
4832 set_canvas_cursor (current_canvas_cursor);
4836 Editor::external_edit_region ()
4842 Editor::reset_region_gain_envelopes ()
4844 RegionSelection rs = get_regions_from_selection_and_entered ();
4846 if (!_session || rs.empty()) {
4850 _session->begin_reversible_command (_("reset region gain"));
4852 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4853 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4855 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4856 XMLNode& before (alist->get_state());
4858 arv->audio_region()->set_default_envelope ();
4859 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4863 _session->commit_reversible_command ();
4867 Editor::set_region_gain_visibility (RegionView* rv)
4869 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4871 arv->update_envelope_visibility();
4876 Editor::set_gain_envelope_visibility ()
4882 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4883 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4885 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4891 Editor::toggle_gain_envelope_active ()
4893 if (_ignore_region_action) {
4897 RegionSelection rs = get_regions_from_selection_and_entered ();
4899 if (!_session || rs.empty()) {
4903 _session->begin_reversible_command (_("region gain envelope active"));
4905 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4906 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4908 arv->region()->clear_changes ();
4909 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4910 _session->add_command (new StatefulDiffCommand (arv->region()));
4914 _session->commit_reversible_command ();
4918 Editor::toggle_region_lock ()
4920 if (_ignore_region_action) {
4924 RegionSelection rs = get_regions_from_selection_and_entered ();
4926 if (!_session || rs.empty()) {
4930 _session->begin_reversible_command (_("toggle region lock"));
4932 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4933 (*i)->region()->clear_changes ();
4934 (*i)->region()->set_locked (!(*i)->region()->locked());
4935 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4938 _session->commit_reversible_command ();
4942 Editor::toggle_region_lock_style ()
4944 if (_ignore_region_action) {
4948 RegionSelection rs = get_regions_from_selection_and_entered ();
4950 if (!_session || rs.empty()) {
4954 _session->begin_reversible_command (_("region lock style"));
4956 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4957 (*i)->region()->clear_changes ();
4958 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4959 (*i)->region()->set_position_lock_style (ns);
4960 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4963 _session->commit_reversible_command ();
4967 Editor::toggle_opaque_region ()
4969 if (_ignore_region_action) {
4973 RegionSelection rs = get_regions_from_selection_and_entered ();
4975 if (!_session || rs.empty()) {
4979 _session->begin_reversible_command (_("change region opacity"));
4981 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4982 (*i)->region()->clear_changes ();
4983 (*i)->region()->set_opaque (!(*i)->region()->opaque());
4984 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4987 _session->commit_reversible_command ();
4991 Editor::toggle_record_enable ()
4993 bool new_state = false;
4995 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4996 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4999 if (!rtav->is_track())
5003 new_state = !rtav->track()->record_enabled();
5007 rtav->track()->set_record_enabled (new_state, this);
5012 Editor::toggle_solo ()
5014 bool new_state = false;
5016 boost::shared_ptr<RouteList> rl (new RouteList);
5018 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5019 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5026 new_state = !rtav->route()->soloed ();
5030 rl->push_back (rtav->route());
5033 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5037 Editor::toggle_mute ()
5039 bool new_state = false;
5041 boost::shared_ptr<RouteList> rl (new RouteList);
5043 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5044 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5051 new_state = !rtav->route()->muted();
5055 rl->push_back (rtav->route());
5058 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5062 Editor::toggle_solo_isolate ()
5067 Editor::set_fade_length (bool in)
5069 RegionSelection rs = get_regions_from_selection_and_entered ();
5075 /* we need a region to measure the offset from the start */
5077 RegionView* rv = rs.front ();
5079 framepos_t pos = get_preferred_edit_position();
5083 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5084 /* edit point is outside the relevant region */
5089 if (pos <= rv->region()->position()) {
5093 len = pos - rv->region()->position();
5094 cmd = _("set fade in length");
5096 if (pos >= rv->region()->last_frame()) {
5100 len = rv->region()->last_frame() - pos;
5101 cmd = _("set fade out length");
5104 begin_reversible_command (cmd);
5106 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5107 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5113 boost::shared_ptr<AutomationList> alist;
5115 alist = tmp->audio_region()->fade_in();
5117 alist = tmp->audio_region()->fade_out();
5120 XMLNode &before = alist->get_state();
5123 tmp->audio_region()->set_fade_in_length (len);
5124 tmp->audio_region()->set_fade_in_active (true);
5126 tmp->audio_region()->set_fade_out_length (len);
5127 tmp->audio_region()->set_fade_out_active (true);
5130 XMLNode &after = alist->get_state();
5131 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5134 commit_reversible_command ();
5138 Editor::set_fade_in_shape (FadeShape shape)
5140 RegionSelection rs = get_regions_from_selection_and_entered ();
5146 begin_reversible_command (_("set fade in shape"));
5148 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5149 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5155 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5156 XMLNode &before = alist->get_state();
5158 tmp->audio_region()->set_fade_in_shape (shape);
5160 XMLNode &after = alist->get_state();
5161 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5164 commit_reversible_command ();
5169 Editor::set_fade_out_shape (FadeShape shape)
5171 RegionSelection rs = get_regions_from_selection_and_entered ();
5177 begin_reversible_command (_("set fade out shape"));
5179 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5180 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5186 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5187 XMLNode &before = alist->get_state();
5189 tmp->audio_region()->set_fade_out_shape (shape);
5191 XMLNode &after = alist->get_state();
5192 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5195 commit_reversible_command ();
5199 Editor::set_fade_in_active (bool yn)
5201 RegionSelection rs = get_regions_from_selection_and_entered ();
5207 begin_reversible_command (_("set fade in active"));
5209 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5210 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5217 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5219 ar->clear_changes ();
5220 ar->set_fade_in_active (yn);
5221 _session->add_command (new StatefulDiffCommand (ar));
5224 commit_reversible_command ();
5228 Editor::set_fade_out_active (bool yn)
5230 RegionSelection rs = get_regions_from_selection_and_entered ();
5236 begin_reversible_command (_("set fade out active"));
5238 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5239 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5245 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5247 ar->clear_changes ();
5248 ar->set_fade_out_active (yn);
5249 _session->add_command(new StatefulDiffCommand (ar));
5252 commit_reversible_command ();
5256 Editor::toggle_region_fades (int dir)
5258 if (_ignore_region_action) {
5262 boost::shared_ptr<AudioRegion> ar;
5265 RegionSelection rs = get_regions_from_selection_and_entered ();
5271 RegionSelection::iterator i;
5272 for (i = rs.begin(); i != rs.end(); ++i) {
5273 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5275 yn = ar->fade_out_active ();
5277 yn = ar->fade_in_active ();
5283 if (i == rs.end()) {
5287 /* XXX should this undo-able? */
5289 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5290 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5293 if (dir == 1 || dir == 0) {
5294 ar->set_fade_in_active (!yn);
5297 if (dir == -1 || dir == 0) {
5298 ar->set_fade_out_active (!yn);
5304 /** Update region fade visibility after its configuration has been changed */
5306 Editor::update_region_fade_visibility ()
5308 bool _fade_visibility = _session->config.get_show_region_fades ();
5310 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5311 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5313 if (_fade_visibility) {
5314 v->audio_view()->show_all_fades ();
5316 v->audio_view()->hide_all_fades ();
5323 Editor::set_edit_point ()
5328 if (!mouse_frame (where, ignored)) {
5334 if (selection->markers.empty()) {
5336 mouse_add_new_marker (where);
5341 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5344 loc->move_to (where);
5350 Editor::set_playhead_cursor ()
5352 if (entered_marker) {
5353 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5358 if (!mouse_frame (where, ignored)) {
5365 _session->request_locate (where, _session->transport_rolling());
5371 Editor::split_region ()
5373 if (((mouse_mode == MouseRange) ||
5374 (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5375 !selection->time.empty()) {
5376 separate_regions_between (selection->time);
5380 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5382 framepos_t where = get_preferred_edit_position ();
5388 split_regions_at (where, rs);
5392 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
5394 if (entered_track && mouse_mode == MouseObject) {
5395 if (!selection->tracks.empty()) {
5396 if (!selection->selected (entered_track)) {
5397 selection->add (entered_track);
5400 /* there is no selection, but this operation requires/prefers selected objects */
5402 if (op_really_wants_one_track_if_none_are_selected) {
5403 selection->set (entered_track);
5409 struct EditorOrderRouteSorter {
5410 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5411 /* use of ">" forces the correct sort order */
5412 return a->order_key ("editor") < b->order_key ("editor");
5417 Editor::select_next_route()
5419 if (selection->tracks.empty()) {
5420 selection->set (track_views.front());
5424 TimeAxisView* current = selection->tracks.front();
5428 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5429 if (*i == current) {
5431 if (i != track_views.end()) {
5434 current = (*(track_views.begin()));
5435 //selection->set (*(track_views.begin()));
5440 rui = dynamic_cast<RouteUI *>(current);
5441 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5443 selection->set(current);
5445 ensure_track_visible(current);
5449 Editor::select_prev_route()
5451 if (selection->tracks.empty()) {
5452 selection->set (track_views.front());
5456 TimeAxisView* current = selection->tracks.front();
5460 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5461 if (*i == current) {
5463 if (i != track_views.rend()) {
5466 current = *(track_views.rbegin());
5471 rui = dynamic_cast<RouteUI *>(current);
5472 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5474 selection->set (current);
5476 ensure_track_visible(current);
5480 Editor::ensure_track_visible(TimeAxisView *track)
5482 if (track->hidden())
5485 double const current_view_min_y = vertical_adjustment.get_value();
5486 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5488 double const track_min_y = track->y_position ();
5489 double const track_max_y = track->y_position () + track->effective_height ();
5491 if (track_min_y >= current_view_min_y &&
5492 track_max_y <= current_view_max_y) {
5498 if (track_min_y < current_view_min_y) {
5499 // Track is above the current view
5500 new_value = track_min_y;
5502 // Track is below the current view
5503 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5506 vertical_adjustment.set_value(new_value);
5510 Editor::set_loop_from_selection (bool play)
5512 if (_session == 0 || selection->time.empty()) {
5516 framepos_t start = selection->time[clicked_selection].start;
5517 framepos_t end = selection->time[clicked_selection].end;
5519 set_loop_range (start, end, _("set loop range from selection"));
5522 _session->request_play_loop (true);
5523 _session->request_locate (start, true);
5528 Editor::set_loop_from_edit_range (bool play)
5530 if (_session == 0) {
5537 if (!get_edit_op_range (start, end)) {
5541 set_loop_range (start, end, _("set loop range from edit range"));
5544 _session->request_play_loop (true);
5545 _session->request_locate (start, true);
5550 Editor::set_loop_from_region (bool play)
5552 framepos_t start = max_framepos;
5555 RegionSelection rs = get_regions_from_selection_and_entered ();
5561 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5562 if ((*i)->region()->position() < start) {
5563 start = (*i)->region()->position();
5565 if ((*i)->region()->last_frame() + 1 > end) {
5566 end = (*i)->region()->last_frame() + 1;
5570 set_loop_range (start, end, _("set loop range from region"));
5573 _session->request_play_loop (true);
5574 _session->request_locate (start, true);
5579 Editor::set_punch_from_selection ()
5581 if (_session == 0 || selection->time.empty()) {
5585 framepos_t start = selection->time[clicked_selection].start;
5586 framepos_t end = selection->time[clicked_selection].end;
5588 set_punch_range (start, end, _("set punch range from selection"));
5592 Editor::set_punch_from_edit_range ()
5594 if (_session == 0) {
5601 if (!get_edit_op_range (start, end)) {
5605 set_punch_range (start, end, _("set punch range from edit range"));
5609 Editor::set_punch_from_region ()
5611 framepos_t start = max_framepos;
5614 RegionSelection rs = get_regions_from_selection_and_entered ();
5620 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5621 if ((*i)->region()->position() < start) {
5622 start = (*i)->region()->position();
5624 if ((*i)->region()->last_frame() + 1 > end) {
5625 end = (*i)->region()->last_frame() + 1;
5629 set_punch_range (start, end, _("set punch range from region"));
5633 Editor::pitch_shift_region ()
5635 RegionSelection rs = get_regions_from_selection_and_entered ();
5637 RegionSelection audio_rs;
5638 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5639 if (dynamic_cast<AudioRegionView*> (*i)) {
5640 audio_rs.push_back (*i);
5644 if (audio_rs.empty()) {
5648 pitch_shift (audio_rs, 1.2);
5652 Editor::transpose_region ()
5654 RegionSelection rs = get_regions_from_selection_and_entered ();
5656 list<MidiRegionView*> midi_region_views;
5657 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5658 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5660 midi_region_views.push_back (mrv);
5665 int const r = d.run ();
5666 if (r != RESPONSE_ACCEPT) {
5670 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5671 (*i)->midi_region()->transpose (d.semitones ());
5676 Editor::set_tempo_from_region ()
5678 RegionSelection rs = get_regions_from_selection_and_entered ();
5680 if (!_session || rs.empty()) {
5684 RegionView* rv = rs.front();
5686 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5690 Editor::use_range_as_bar ()
5692 framepos_t start, end;
5693 if (get_edit_op_range (start, end)) {
5694 define_one_bar (start, end);
5699 Editor::define_one_bar (framepos_t start, framepos_t end)
5701 framepos_t length = end - start;
5703 const Meter& m (_session->tempo_map().meter_at (start));
5705 /* length = 1 bar */
5707 /* now we want frames per beat.
5708 we have frames per bar, and beats per bar, so ...
5711 /* XXXX METER MATH */
5713 double frames_per_beat = length / m.divisions_per_bar();
5715 /* beats per minute = */
5717 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5719 /* now decide whether to:
5721 (a) set global tempo
5722 (b) add a new tempo marker
5726 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5728 bool do_global = false;
5730 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5732 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5733 at the start, or create a new marker
5736 vector<string> options;
5737 options.push_back (_("Cancel"));
5738 options.push_back (_("Add new marker"));
5739 options.push_back (_("Set global tempo"));
5742 _("Define one bar"),
5743 _("Do you want to set the global tempo or add a new tempo marker?"),
5747 c.set_default_response (2);
5763 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5764 if the marker is at the region starter, change it, otherwise add
5769 begin_reversible_command (_("set tempo from region"));
5770 XMLNode& before (_session->tempo_map().get_state());
5773 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5774 } else if (t.frame() == start) {
5775 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5777 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5780 XMLNode& after (_session->tempo_map().get_state());
5782 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5783 commit_reversible_command ();
5787 Editor::split_region_at_transients ()
5789 AnalysisFeatureList positions;
5791 RegionSelection rs = get_regions_from_selection_and_entered ();
5793 if (!_session || rs.empty()) {
5797 _session->begin_reversible_command (_("split regions"));
5799 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5801 RegionSelection::iterator tmp;
5806 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5808 if (ar && (ar->get_transients (positions) == 0)) {
5809 split_region_at_points ((*i)->region(), positions, true);
5816 _session->commit_reversible_command ();
5821 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5823 bool use_rhythmic_rodent = false;
5825 boost::shared_ptr<Playlist> pl = r->playlist();
5827 list<boost::shared_ptr<Region> > new_regions;
5833 if (positions.empty()) {
5838 if (positions.size() > 20 && can_ferret) {
5839 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);
5840 MessageDialog msg (msgstr,
5843 Gtk::BUTTONS_OK_CANCEL);
5846 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5847 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5849 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5852 msg.set_title (_("Excessive split?"));
5855 int response = msg.run();
5861 case RESPONSE_APPLY:
5862 use_rhythmic_rodent = true;
5869 if (use_rhythmic_rodent) {
5870 show_rhythm_ferret ();
5874 AnalysisFeatureList::const_iterator x;
5876 pl->clear_changes ();
5877 pl->clear_owned_changes ();
5879 x = positions.begin();
5881 if (x == positions.end()) {
5886 pl->remove_region (r);
5890 while (x != positions.end()) {
5892 /* deal with positons that are out of scope of present region bounds */
5893 if (*x <= 0 || *x > r->length()) {
5898 /* file start = original start + how far we from the initial position ?
5901 framepos_t file_start = r->start() + pos;
5903 /* length = next position - current position
5906 framepos_t len = (*x) - pos;
5908 /* XXX we do we really want to allow even single-sample regions?
5909 shouldn't we have some kind of lower limit on region size?
5918 if (RegionFactory::region_name (new_name, r->name())) {
5922 /* do NOT announce new regions 1 by one, just wait till they are all done */
5926 plist.add (ARDOUR::Properties::start, file_start);
5927 plist.add (ARDOUR::Properties::length, len);
5928 plist.add (ARDOUR::Properties::name, new_name);
5929 plist.add (ARDOUR::Properties::layer, 0);
5931 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5932 /* because we set annouce to false, manually add the new region to the
5935 RegionFactory::map_add (nr);
5937 pl->add_region (nr, r->position() + pos);
5940 new_regions.push_front(nr);
5949 RegionFactory::region_name (new_name, r->name());
5951 /* Add the final region */
5954 plist.add (ARDOUR::Properties::start, r->start() + pos);
5955 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5956 plist.add (ARDOUR::Properties::name, new_name);
5957 plist.add (ARDOUR::Properties::layer, 0);
5959 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5960 /* because we set annouce to false, manually add the new region to the
5963 RegionFactory::map_add (nr);
5964 pl->add_region (nr, r->position() + pos);
5967 new_regions.push_front(nr);
5972 /* We might have removed regions, which alters other regions' layering_index,
5973 so we need to do a recursive diff here.
5975 vector<Command*> cmds;
5977 _session->add_commands (cmds);
5979 _session->add_command (new StatefulDiffCommand (pl));
5983 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5984 set_selected_regionview_from_region_list ((*i), Selection::Add);
5990 Editor::place_transient()
5996 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6002 framepos_t where = get_preferred_edit_position();
6004 _session->begin_reversible_command (_("place transient"));
6006 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6007 framepos_t position = (*r)->region()->position();
6008 (*r)->region()->add_transient(where - position);
6011 _session->commit_reversible_command ();
6015 Editor::remove_transient(ArdourCanvas::Item* item)
6021 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6024 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6025 _arv->remove_transient (*(float*) _line->get_data ("position"));
6029 Editor::snap_regions_to_grid ()
6031 list <boost::shared_ptr<Playlist > > used_playlists;
6033 RegionSelection rs = get_regions_from_selection_and_entered ();
6035 if (!_session || rs.empty()) {
6039 _session->begin_reversible_command (_("snap regions to grid"));
6041 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6043 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6045 if (!pl->frozen()) {
6046 /* we haven't seen this playlist before */
6048 /* remember used playlists so we can thaw them later */
6049 used_playlists.push_back(pl);
6053 framepos_t start_frame = (*r)->region()->first_frame ();
6054 snap_to (start_frame);
6055 (*r)->region()->set_position (start_frame);
6058 while (used_playlists.size() > 0) {
6059 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6061 used_playlists.pop_front();
6064 _session->commit_reversible_command ();
6068 Editor::close_region_gaps ()
6070 list <boost::shared_ptr<Playlist > > used_playlists;
6072 RegionSelection rs = get_regions_from_selection_and_entered ();
6074 if (!_session || rs.empty()) {
6078 Dialog dialog (_("Close Region Gaps"));
6081 table.set_spacings (12);
6082 table.set_border_width (12);
6083 Label* l = manage (new Label (_("Crossfade length")));
6084 l->set_alignment (0, 0.5);
6085 table.attach (*l, 0, 1, 0, 1);
6087 SpinButton spin_crossfade (1, 0);
6088 spin_crossfade.set_range (0, 15);
6089 spin_crossfade.set_increments (1, 1);
6090 spin_crossfade.set_value (5);
6091 table.attach (spin_crossfade, 1, 2, 0, 1);
6093 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6095 l = manage (new Label (_("Pull-back length")));
6096 l->set_alignment (0, 0.5);
6097 table.attach (*l, 0, 1, 1, 2);
6099 SpinButton spin_pullback (1, 0);
6100 spin_pullback.set_range (0, 100);
6101 spin_pullback.set_increments (1, 1);
6102 spin_pullback.set_value(30);
6103 table.attach (spin_pullback, 1, 2, 1, 2);
6105 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6107 dialog.get_vbox()->pack_start (table);
6108 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6109 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6112 if (dialog.run () == RESPONSE_CANCEL) {
6116 framepos_t crossfade_len = spin_crossfade.get_value();
6117 framepos_t pull_back_frames = spin_pullback.get_value();
6119 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6120 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6122 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6124 _session->begin_reversible_command (_("close region gaps"));
6127 boost::shared_ptr<Region> last_region;
6129 rs.sort_by_position_and_track();
6131 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6133 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6135 if (!pl->frozen()) {
6136 /* we haven't seen this playlist before */
6138 /* remember used playlists so we can thaw them later */
6139 used_playlists.push_back(pl);
6143 framepos_t position = (*r)->region()->position();
6145 if (idx == 0 || position < last_region->position()){
6146 last_region = (*r)->region();
6151 (*r)->region()->trim_front( (position - pull_back_frames));
6152 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6154 last_region = (*r)->region();
6159 while (used_playlists.size() > 0) {
6160 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6162 used_playlists.pop_front();
6165 _session->commit_reversible_command ();
6169 Editor::tab_to_transient (bool forward)
6171 AnalysisFeatureList positions;
6173 RegionSelection rs = get_regions_from_selection_and_entered ();
6179 framepos_t pos = _session->audible_frame ();
6181 if (!selection->tracks.empty()) {
6183 /* don't waste time searching for transients in duplicate playlists.
6186 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6188 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6190 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6193 boost::shared_ptr<Track> tr = rtv->track();
6195 boost::shared_ptr<Playlist> pl = tr->playlist ();
6197 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6200 positions.push_back (result);
6213 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6214 (*r)->region()->get_transients (positions);
6218 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6221 AnalysisFeatureList::iterator x;
6223 for (x = positions.begin(); x != positions.end(); ++x) {
6229 if (x != positions.end ()) {
6230 _session->request_locate (*x);
6234 AnalysisFeatureList::reverse_iterator x;
6236 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6242 if (x != positions.rend ()) {
6243 _session->request_locate (*x);
6249 Editor::playhead_forward_to_grid ()
6251 if (!_session) return;
6252 framepos_t pos = playhead_cursor->current_frame;
6253 if (pos < max_framepos - 1) {
6255 snap_to_internal (pos, 1, false);
6256 _session->request_locate (pos);
6262 Editor::playhead_backward_to_grid ()
6264 if (!_session) return;
6265 framepos_t pos = playhead_cursor->current_frame;
6268 snap_to_internal (pos, -1, false);
6269 _session->request_locate (pos);
6274 Editor::set_track_height (Height h)
6276 TrackSelection& ts (selection->tracks);
6278 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6279 (*x)->set_height_enum (h);
6284 Editor::toggle_tracks_active ()
6286 TrackSelection& ts (selection->tracks);
6288 bool target = false;
6294 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6295 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6299 target = !rtv->_route->active();
6302 rtv->_route->set_active (target, this);
6308 Editor::remove_tracks ()
6310 TrackSelection& ts (selection->tracks);
6316 vector<string> choices;
6320 const char* trackstr;
6322 vector<boost::shared_ptr<Route> > routes;
6323 bool special_bus = false;
6325 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6326 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6328 if (rtv->is_track()) {
6334 routes.push_back (rtv->_route);
6336 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6341 if (special_bus && !Config->get_allow_special_bus_removal()) {
6342 MessageDialog msg (_("That would be bad news ...."),
6346 msg.set_secondary_text (string_compose (_(
6347 "Removing the master or monitor bus is such a bad idea\n\
6348 that %1 is not going to allow it.\n\
6350 If you really want to do this sort of thing\n\
6351 edit your ardour.rc file to set the\n\
6352 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6359 if (ntracks + nbusses == 0) {
6364 trackstr = _("tracks");
6366 trackstr = _("track");
6370 busstr = _("busses");
6377 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6378 "(You may also lose the playlists associated with the %2)\n\n"
6379 "This action cannot be undone, and the session file will be overwritten!"),
6380 ntracks, trackstr, nbusses, busstr);
6382 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6383 "(You may also lose the playlists associated with the %2)\n\n"
6384 "This action cannot be undone, and the session file will be overwritten!"),
6387 } else if (nbusses) {
6388 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6389 "This action cannot be undon, and the session file will be overwritten"),
6393 choices.push_back (_("No, do nothing."));
6394 if (ntracks + nbusses > 1) {
6395 choices.push_back (_("Yes, remove them."));
6397 choices.push_back (_("Yes, remove it."));
6402 title = string_compose (_("Remove %1"), trackstr);
6404 title = string_compose (_("Remove %1"), busstr);
6407 Choice prompter (title, prompt, choices);
6409 if (prompter.run () != 1) {
6413 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6414 _session->remove_route (*x);
6419 Editor::do_insert_time ()
6421 if (selection->tracks.empty()) {
6425 InsertTimeDialog d (*this);
6426 int response = d.run ();
6428 if (response != RESPONSE_OK) {
6432 if (d.distance() == 0) {
6436 InsertTimeOption opt = d.intersected_region_action ();
6439 get_preferred_edit_position(),
6445 d.move_glued_markers(),
6446 d.move_locked_markers(),
6452 Editor::insert_time (
6453 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6454 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6457 bool commit = false;
6459 if (Config->get_edit_mode() == Lock) {
6463 begin_reversible_command (_("insert time"));
6465 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6467 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6471 /* don't operate on any playlist more than once, which could
6472 * happen if "all playlists" is enabled, but there is more
6473 * than 1 track using playlists "from" a given track.
6476 set<boost::shared_ptr<Playlist> > pl;
6478 if (all_playlists) {
6479 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6481 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6482 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6487 if ((*x)->playlist ()) {
6488 pl.insert ((*x)->playlist ());
6492 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6494 (*i)->clear_changes ();
6495 (*i)->clear_owned_changes ();
6497 if (opt == SplitIntersected) {
6501 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6503 vector<Command*> cmds;
6505 _session->add_commands (cmds);
6507 _session->add_command (new StatefulDiffCommand (*i));
6512 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6514 rtav->route ()->shift (pos, frames);
6522 XMLNode& before (_session->locations()->get_state());
6523 Locations::LocationList copy (_session->locations()->list());
6525 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6527 Locations::LocationList::const_iterator tmp;
6529 bool const was_locked = (*i)->locked ();
6530 if (locked_markers_too) {
6534 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6536 if ((*i)->start() >= pos) {
6537 (*i)->set_start ((*i)->start() + frames);
6538 if (!(*i)->is_mark()) {
6539 (*i)->set_end ((*i)->end() + frames);
6552 XMLNode& after (_session->locations()->get_state());
6553 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6558 _session->tempo_map().insert_time (pos, frames);
6562 commit_reversible_command ();
6567 Editor::fit_selected_tracks ()
6569 if (!selection->tracks.empty()) {
6570 fit_tracks (selection->tracks);
6574 /* no selected tracks - use tracks with selected regions */
6576 if (!selection->regions.empty()) {
6577 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6578 tvl.push_back (&(*r)->get_time_axis_view ());
6584 } else if (internal_editing()) {
6585 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6588 if (entered_track) {
6589 tvl.push_back (entered_track);
6597 Editor::fit_tracks (TrackViewList & tracks)
6599 if (tracks.empty()) {
6603 uint32_t child_heights = 0;
6604 int visible_tracks = 0;
6606 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6608 if (!(*t)->marked_for_display()) {
6612 child_heights += (*t)->effective_height() - (*t)->current_height();
6616 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6617 double first_y_pos = DBL_MAX;
6619 if (h < TimeAxisView::preset_height (HeightSmall)) {
6620 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6621 /* too small to be displayed */
6625 undo_visual_stack.push_back (current_visual_state (true));
6626 no_save_visual = true;
6628 /* build a list of all tracks, including children */
6631 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6633 TimeAxisView::Children c = (*i)->get_child_list ();
6634 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6635 all.push_back (j->get());
6639 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6641 bool prev_was_selected = false;
6642 bool is_selected = tracks.contains (all.front());
6643 bool next_is_selected;
6645 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6647 TrackViewList::iterator next;
6652 if (next != all.end()) {
6653 next_is_selected = tracks.contains (*next);
6655 next_is_selected = false;
6658 if ((*t)->marked_for_display ()) {
6660 (*t)->set_height (h);
6661 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6663 if (prev_was_selected && next_is_selected) {
6664 hide_track_in_display (*t);
6669 prev_was_selected = is_selected;
6670 is_selected = next_is_selected;
6674 set the controls_layout height now, because waiting for its size
6675 request signal handler will cause the vertical adjustment setting to fail
6678 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6679 vertical_adjustment.set_value (first_y_pos);
6681 redo_visual_stack.push_back (current_visual_state (true));
6685 Editor::save_visual_state (uint32_t n)
6687 while (visual_states.size() <= n) {
6688 visual_states.push_back (0);
6691 if (visual_states[n] != 0) {
6692 delete visual_states[n];
6695 visual_states[n] = current_visual_state (true);
6700 Editor::goto_visual_state (uint32_t n)
6702 if (visual_states.size() <= n) {
6706 if (visual_states[n] == 0) {
6710 use_visual_state (*visual_states[n]);
6714 Editor::start_visual_state_op (uint32_t n)
6716 save_visual_state (n);
6718 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6720 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6721 pup->set_text (buf);
6726 Editor::cancel_visual_state_op (uint32_t n)
6728 goto_visual_state (n);
6732 Editor::toggle_region_mute ()
6734 if (_ignore_region_action) {
6738 RegionSelection rs = get_regions_from_selection_and_entered ();
6744 if (rs.size() > 1) {
6745 begin_reversible_command (_("mute regions"));
6747 begin_reversible_command (_("mute region"));
6750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6752 (*i)->region()->playlist()->clear_changes ();
6753 (*i)->region()->set_muted (!(*i)->region()->muted ());
6754 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6758 commit_reversible_command ();
6762 Editor::combine_regions ()
6764 /* foreach track with selected regions, take all selected regions
6765 and join them into a new region containing the subregions (as a
6769 typedef set<RouteTimeAxisView*> RTVS;
6772 if (selection->regions.empty()) {
6776 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6777 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6780 tracks.insert (rtv);
6784 begin_reversible_command (_("combine regions"));
6786 vector<RegionView*> new_selection;
6788 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6791 if ((rv = (*i)->combine_regions ()) != 0) {
6792 new_selection.push_back (rv);
6796 selection->clear_regions ();
6797 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6798 selection->add (*i);
6801 commit_reversible_command ();
6805 Editor::uncombine_regions ()
6807 typedef set<RouteTimeAxisView*> RTVS;
6810 if (selection->regions.empty()) {
6814 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6815 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6818 tracks.insert (rtv);
6822 begin_reversible_command (_("uncombine regions"));
6824 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6825 (*i)->uncombine_regions ();
6828 commit_reversible_command ();