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::clamp_frames_per_unit (double& fpu) const
1333 bool clamped = false;
1340 if (max_framepos / fpu < 800) {
1341 fpu = max_framepos / 800.0;
1349 Editor::temporal_zoom_step (bool coarser)
1351 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1353 double nfpu = frames_per_unit;
1356 nfpu = min (9e6, nfpu * 1.61803399);
1358 nfpu = max (1.0, nfpu / 1.61803399);
1361 temporal_zoom (nfpu);
1365 Editor::temporal_zoom (double fpu)
1371 framepos_t current_page = current_page_frames();
1372 framepos_t current_leftmost = leftmost_frame;
1373 framepos_t current_rightmost;
1374 framepos_t current_center;
1375 framepos_t new_page_size;
1376 framepos_t half_page_size;
1377 framepos_t leftmost_after_zoom = 0;
1379 bool in_track_canvas;
1383 clamp_frames_per_unit (fpu);
1384 if (fpu == frames_per_unit) {
1390 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1391 // segfaults for lack of memory. If somebody decides this is not high enough I
1392 // believe it can be raisen to higher values but some limit must be in place.
1397 new_page_size = (framepos_t) floor (_canvas_width * nfpu);
1398 half_page_size = new_page_size / 2;
1400 switch (zoom_focus) {
1402 leftmost_after_zoom = current_leftmost;
1405 case ZoomFocusRight:
1406 current_rightmost = leftmost_frame + current_page;
1407 if (current_rightmost < new_page_size) {
1408 leftmost_after_zoom = 0;
1410 leftmost_after_zoom = current_rightmost - new_page_size;
1414 case ZoomFocusCenter:
1415 current_center = current_leftmost + (current_page/2);
1416 if (current_center < half_page_size) {
1417 leftmost_after_zoom = 0;
1419 leftmost_after_zoom = current_center - half_page_size;
1423 case ZoomFocusPlayhead:
1424 /* centre playhead */
1425 l = playhead_cursor->current_frame - (new_page_size * 0.5);
1428 leftmost_after_zoom = 0;
1429 } else if (l > max_framepos) {
1430 leftmost_after_zoom = max_framepos - new_page_size;
1432 leftmost_after_zoom = (framepos_t) l;
1436 case ZoomFocusMouse:
1437 /* try to keep the mouse over the same point in the display */
1439 if (!mouse_frame (where, in_track_canvas)) {
1440 /* use playhead instead */
1441 where = playhead_cursor->current_frame;
1443 if (where < half_page_size) {
1444 leftmost_after_zoom = 0;
1446 leftmost_after_zoom = where - half_page_size;
1451 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1454 leftmost_after_zoom = 0;
1455 } else if (l > max_framepos) {
1456 leftmost_after_zoom = max_framepos - new_page_size;
1458 leftmost_after_zoom = (framepos_t) l;
1465 /* try to keep the edit point in the same place */
1466 where = get_preferred_edit_position ();
1470 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1473 leftmost_after_zoom = 0;
1474 } else if (l > max_framepos) {
1475 leftmost_after_zoom = max_framepos - new_page_size;
1477 leftmost_after_zoom = (framepos_t) l;
1481 /* edit point not defined */
1488 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1490 reposition_and_zoom (leftmost_after_zoom, nfpu);
1494 Editor::temporal_zoom_region (bool both_axes)
1496 framepos_t start = max_framepos;
1498 set<TimeAxisView*> tracks;
1500 RegionSelection rs = get_regions_from_selection_and_entered ();
1506 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1508 if ((*i)->region()->position() < start) {
1509 start = (*i)->region()->position();
1512 if ((*i)->region()->last_frame() + 1 > end) {
1513 end = (*i)->region()->last_frame() + 1;
1516 tracks.insert (&((*i)->get_time_axis_view()));
1519 /* now comes an "interesting" hack ... make sure we leave a little space
1520 at each end of the editor so that the zoom doesn't fit the region
1521 precisely to the screen.
1524 GdkScreen* screen = gdk_screen_get_default ();
1525 gint pixwidth = gdk_screen_get_width (screen);
1526 gint mmwidth = gdk_screen_get_width_mm (screen);
1527 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1528 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1530 if ((start == 0 && end == 0) || end < start) {
1534 framepos_t range = end - start;
1535 double new_fpu = (double)range / (double)_canvas_width;
1536 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
1538 if (start > extra_samples) {
1539 start -= extra_samples;
1544 if (max_framepos - extra_samples > end) {
1545 end += extra_samples;
1550 /* if we're zooming on both axes we need to save track heights etc.
1553 undo_visual_stack.push_back (current_visual_state (both_axes));
1555 PBD::Unwinder<bool> nsv (no_save_visual, true);
1557 temporal_zoom_by_frame (start, end);
1560 uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1562 /* set visible track heights appropriately */
1564 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1565 (*t)->set_height (per_track_height);
1568 /* hide irrelevant tracks */
1570 _routes->suspend_redisplay ();
1572 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1573 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1574 hide_track_in_display (*i);
1578 _routes->resume_redisplay ();
1580 vertical_adjustment.set_value (0.0);
1583 redo_visual_stack.push_back (current_visual_state (both_axes));
1587 Editor::zoom_to_region (bool both_axes)
1589 temporal_zoom_region (both_axes);
1593 Editor::temporal_zoom_selection ()
1595 if (!selection) return;
1597 if (selection->time.empty()) {
1601 framepos_t start = selection->time[clicked_selection].start;
1602 framepos_t end = selection->time[clicked_selection].end;
1604 temporal_zoom_by_frame (start, end);
1608 Editor::temporal_zoom_session ()
1610 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1613 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1614 double s = _session->current_start_frame() - l * 0.01;
1618 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1619 temporal_zoom_by_frame (framecnt_t (s), e);
1624 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1626 if (!_session) return;
1628 if ((start == 0 && end == 0) || end < start) {
1632 framepos_t range = end - start;
1634 double new_fpu = (double)range / (double)_canvas_width;
1636 framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
1637 framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
1638 framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
1640 if (new_leftmost > middle) {
1644 if (new_leftmost < 0) {
1648 reposition_and_zoom (new_leftmost, new_fpu);
1652 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1657 double range_before = frame - leftmost_frame;
1660 new_fpu = frames_per_unit;
1663 new_fpu *= 1.61803399;
1664 range_before *= 1.61803399;
1666 new_fpu = max(1.0,(new_fpu/1.61803399));
1667 range_before /= 1.61803399;
1670 if (new_fpu == frames_per_unit) {
1674 framepos_t new_leftmost = frame - (framepos_t)range_before;
1676 if (new_leftmost > frame) {
1680 if (new_leftmost < 0) {
1684 reposition_and_zoom (new_leftmost, new_fpu);
1689 Editor::choose_new_marker_name(string &name) {
1691 if (!Config->get_name_new_markers()) {
1692 /* don't prompt user for a new name */
1696 ArdourPrompter dialog (true);
1698 dialog.set_prompt (_("New Name:"));
1700 dialog.set_title (_("New Location Marker"));
1702 dialog.set_name ("MarkNameWindow");
1703 dialog.set_size_request (250, -1);
1704 dialog.set_position (Gtk::WIN_POS_MOUSE);
1706 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1707 dialog.set_initial_text (name);
1711 switch (dialog.run ()) {
1712 case RESPONSE_ACCEPT:
1718 dialog.get_result(name);
1725 Editor::add_location_from_selection ()
1729 if (selection->time.empty()) {
1733 if (_session == 0 || clicked_axisview == 0) {
1737 framepos_t start = selection->time[clicked_selection].start;
1738 framepos_t end = selection->time[clicked_selection].end;
1740 _session->locations()->next_available_name(rangename,"selection");
1741 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1743 _session->begin_reversible_command (_("add marker"));
1744 XMLNode &before = _session->locations()->get_state();
1745 _session->locations()->add (location, true);
1746 XMLNode &after = _session->locations()->get_state();
1747 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1748 _session->commit_reversible_command ();
1752 Editor::add_location_mark (framepos_t where)
1756 select_new_marker = true;
1758 _session->locations()->next_available_name(markername,"mark");
1759 if (!choose_new_marker_name(markername)) {
1762 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1763 _session->begin_reversible_command (_("add marker"));
1764 XMLNode &before = _session->locations()->get_state();
1765 _session->locations()->add (location, true);
1766 XMLNode &after = _session->locations()->get_state();
1767 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1768 _session->commit_reversible_command ();
1772 Editor::add_location_from_playhead_cursor ()
1774 add_location_mark (_session->audible_frame());
1777 /** Add a range marker around each selected region */
1779 Editor::add_locations_from_region ()
1781 RegionSelection rs = get_regions_from_selection_and_entered ();
1787 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1788 XMLNode &before = _session->locations()->get_state();
1790 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1792 boost::shared_ptr<Region> region = (*i)->region ();
1794 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1796 _session->locations()->add (location, true);
1799 XMLNode &after = _session->locations()->get_state();
1800 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1801 _session->commit_reversible_command ();
1804 /** Add a single range marker around all selected regions */
1806 Editor::add_location_from_region ()
1808 RegionSelection rs = get_regions_from_selection_and_entered ();
1814 _session->begin_reversible_command (_("add marker"));
1815 XMLNode &before = _session->locations()->get_state();
1819 if (rs.size() > 1) {
1820 _session->locations()->next_available_name(markername, "regions");
1822 RegionView* rv = *(rs.begin());
1823 boost::shared_ptr<Region> region = rv->region();
1824 markername = region->name();
1827 if (!choose_new_marker_name(markername)) {
1831 // single range spanning all selected
1832 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1833 _session->locations()->add (location, true);
1835 XMLNode &after = _session->locations()->get_state();
1836 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1837 _session->commit_reversible_command ();
1843 Editor::jump_forward_to_mark ()
1849 Location *location = _session->locations()->first_location_after (playhead_cursor->current_frame);
1852 _session->request_locate (location->start(), _session->transport_rolling());
1854 _session->request_locate (_session->current_end_frame());
1859 Editor::jump_backward_to_mark ()
1865 Location *location = _session->locations()->first_location_before (playhead_cursor->current_frame);
1868 _session->request_locate (location->start(), _session->transport_rolling());
1870 _session->goto_start ();
1877 framepos_t const pos = _session->audible_frame ();
1880 _session->locations()->next_available_name (markername, "mark");
1882 if (!choose_new_marker_name (markername)) {
1886 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1890 Editor::clear_markers ()
1893 _session->begin_reversible_command (_("clear markers"));
1894 XMLNode &before = _session->locations()->get_state();
1895 _session->locations()->clear_markers ();
1896 XMLNode &after = _session->locations()->get_state();
1897 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1898 _session->commit_reversible_command ();
1903 Editor::clear_ranges ()
1906 _session->begin_reversible_command (_("clear ranges"));
1907 XMLNode &before = _session->locations()->get_state();
1909 Location * looploc = _session->locations()->auto_loop_location();
1910 Location * punchloc = _session->locations()->auto_punch_location();
1911 Location * sessionloc = _session->locations()->session_range_location();
1913 _session->locations()->clear_ranges ();
1915 if (looploc) _session->locations()->add (looploc);
1916 if (punchloc) _session->locations()->add (punchloc);
1917 if (sessionloc) _session->locations()->add (sessionloc);
1919 XMLNode &after = _session->locations()->get_state();
1920 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1921 _session->commit_reversible_command ();
1926 Editor::clear_locations ()
1928 _session->begin_reversible_command (_("clear locations"));
1929 XMLNode &before = _session->locations()->get_state();
1930 _session->locations()->clear ();
1931 XMLNode &after = _session->locations()->get_state();
1932 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1933 _session->commit_reversible_command ();
1934 _session->locations()->clear ();
1938 Editor::unhide_markers ()
1940 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1941 Location *l = (*i).first;
1942 if (l->is_hidden() && l->is_mark()) {
1943 l->set_hidden(false, this);
1949 Editor::unhide_ranges ()
1951 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1952 Location *l = (*i).first;
1953 if (l->is_hidden() && l->is_range_marker()) {
1954 l->set_hidden(false, this);
1959 /* INSERT/REPLACE */
1962 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1967 RouteTimeAxisView *rtv = 0;
1968 boost::shared_ptr<Playlist> playlist;
1970 track_canvas->window_to_world (x, y, wx, wy);
1973 event.type = GDK_BUTTON_RELEASE;
1974 event.button.x = wx;
1975 event.button.y = wy;
1977 where = event_frame (&event, &cx, &cy);
1979 if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1980 /* clearly outside canvas area */
1984 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1985 if (tv.first == 0) {
1989 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1993 if ((playlist = rtv->playlist()) == 0) {
1999 begin_reversible_command (_("insert dragged region"));
2000 playlist->clear_changes ();
2001 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
2002 _session->add_command(new StatefulDiffCommand (playlist));
2003 commit_reversible_command ();
2007 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
2011 RouteTimeAxisView *dest_rtv = 0;
2012 RouteTimeAxisView *source_rtv = 0;
2014 track_canvas->window_to_world (x, y, wx, wy);
2015 wx += horizontal_position ();
2016 wy += vertical_adjustment.get_value();
2019 event.type = GDK_BUTTON_RELEASE;
2020 event.button.x = wx;
2021 event.button.y = wy;
2023 event_frame (&event, &cx, &cy);
2025 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2026 if (tv.first == 0) {
2030 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2034 /* use this drag source to add underlay to a track. But we really don't care
2035 about the Route, only the view of the route, so find it first */
2036 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2037 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2041 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2042 dest_rtv->add_underlay(source_rtv->view());
2049 Editor::insert_region_list_selection (float times)
2051 RouteTimeAxisView *tv = 0;
2052 boost::shared_ptr<Playlist> playlist;
2054 if (clicked_routeview != 0) {
2055 tv = clicked_routeview;
2056 } else if (!selection->tracks.empty()) {
2057 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2060 } else if (entered_track != 0) {
2061 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2068 if ((playlist = tv->playlist()) == 0) {
2072 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2077 begin_reversible_command (_("insert region"));
2078 playlist->clear_changes ();
2079 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2080 _session->add_command(new StatefulDiffCommand (playlist));
2081 commit_reversible_command ();
2084 /* BUILT-IN EFFECTS */
2087 Editor::reverse_selection ()
2092 /* GAIN ENVELOPE EDITING */
2095 Editor::edit_envelope ()
2102 Editor::transition_to_rolling (bool fwd)
2108 if (_session->config.get_external_sync()) {
2109 switch (_session->config.get_sync_source()) {
2113 /* transport controlled by the master */
2118 if (_session->is_auditioning()) {
2119 _session->cancel_audition ();
2123 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2127 Editor::play_from_start ()
2129 _session->request_locate (_session->current_start_frame(), true);
2133 Editor::play_from_edit_point ()
2135 _session->request_locate (get_preferred_edit_position(), true);
2139 Editor::play_from_edit_point_and_return ()
2141 framepos_t start_frame;
2142 framepos_t return_frame;
2144 start_frame = get_preferred_edit_position (true);
2146 if (_session->transport_rolling()) {
2147 _session->request_locate (start_frame, false);
2151 /* don't reset the return frame if its already set */
2153 if ((return_frame = _session->requested_return_frame()) < 0) {
2154 return_frame = _session->audible_frame();
2157 if (start_frame >= 0) {
2158 _session->request_roll_at_and_return (start_frame, return_frame);
2163 Editor::play_selection ()
2165 if (selection->time.empty()) {
2169 _session->request_play_range (&selection->time, true);
2173 Editor::play_location (Location& location)
2175 if (location.start() <= location.end()) {
2179 _session->request_bounded_roll (location.start(), location.end());
2183 Editor::loop_location (Location& location)
2185 if (location.start() <= location.end()) {
2191 if ((tll = transport_loop_location()) != 0) {
2192 tll->set (location.start(), location.end());
2194 // enable looping, reposition and start rolling
2195 _session->request_play_loop (true);
2196 _session->request_locate (tll->start(), true);
2201 Editor::do_layer_operation (LayerOperation op)
2203 if (selection->regions.empty ()) {
2207 bool const multiple = selection->regions.size() > 1;
2211 begin_reversible_command (_("raise regions"));
2213 begin_reversible_command (_("raise region"));
2219 begin_reversible_command (_("raise regions to top"));
2221 begin_reversible_command (_("raise region to top"));
2227 begin_reversible_command (_("lower regions"));
2229 begin_reversible_command (_("lower region"));
2235 begin_reversible_command (_("lower regions to bottom"));
2237 begin_reversible_command (_("lower region"));
2242 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2243 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2244 (*i)->clear_owned_changes ();
2247 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2248 boost::shared_ptr<Region> r = (*i)->region ();
2260 r->lower_to_bottom ();
2264 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2265 vector<Command*> cmds;
2267 _session->add_commands (cmds);
2270 commit_reversible_command ();
2274 Editor::raise_region ()
2276 do_layer_operation (Raise);
2280 Editor::raise_region_to_top ()
2282 do_layer_operation (RaiseToTop);
2286 Editor::lower_region ()
2288 do_layer_operation (Lower);
2292 Editor::lower_region_to_bottom ()
2294 do_layer_operation (LowerToBottom);
2297 /** Show the region editor for the selected regions */
2299 Editor::show_region_properties ()
2301 selection->foreach_regionview (&RegionView::show_region_editor);
2304 /** Show the midi list editor for the selected MIDI regions */
2306 Editor::show_midi_list_editor ()
2308 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2312 Editor::rename_region ()
2314 RegionSelection rs = get_regions_from_selection_and_entered ();
2320 ArdourDialog d (*this, _("Rename Region"), true, false);
2322 Label label (_("New name:"));
2325 hbox.set_spacing (6);
2326 hbox.pack_start (label, false, false);
2327 hbox.pack_start (entry, true, true);
2329 d.get_vbox()->set_border_width (12);
2330 d.get_vbox()->pack_start (hbox, false, false);
2332 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2333 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2335 d.set_size_request (300, -1);
2336 d.set_position (Gtk::WIN_POS_MOUSE);
2338 entry.set_text (rs.front()->region()->name());
2339 entry.select_region (0, -1);
2341 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2347 int const ret = d.run();
2351 if (ret != RESPONSE_OK) {
2355 std::string str = entry.get_text();
2356 strip_whitespace_edges (str);
2358 rs.front()->region()->set_name (str);
2359 _regions->redisplay ();
2364 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2366 if (_session->is_auditioning()) {
2367 _session->cancel_audition ();
2370 // note: some potential for creativity here, because region doesn't
2371 // have to belong to the playlist that Route is handling
2373 // bool was_soloed = route.soloed();
2375 route.set_solo (true, this);
2377 _session->request_bounded_roll (region->position(), region->position() + region->length());
2379 /* XXX how to unset the solo state ? */
2382 /** Start an audition of the first selected region */
2384 Editor::play_edit_range ()
2386 framepos_t start, end;
2388 if (get_edit_op_range (start, end)) {
2389 _session->request_bounded_roll (start, end);
2394 Editor::play_selected_region ()
2396 framepos_t start = max_framepos;
2399 RegionSelection rs = get_regions_from_selection_and_entered ();
2405 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2406 if ((*i)->region()->position() < start) {
2407 start = (*i)->region()->position();
2409 if ((*i)->region()->last_frame() + 1 > end) {
2410 end = (*i)->region()->last_frame() + 1;
2414 _session->request_bounded_roll (start, end);
2418 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2420 _session->audition_region (region);
2424 Editor::region_from_selection ()
2426 if (clicked_axisview == 0) {
2430 if (selection->time.empty()) {
2434 framepos_t start = selection->time[clicked_selection].start;
2435 framepos_t end = selection->time[clicked_selection].end;
2437 TrackViewList tracks = get_tracks_for_range_action ();
2439 framepos_t selection_cnt = end - start + 1;
2441 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2442 boost::shared_ptr<Region> current;
2443 boost::shared_ptr<Playlist> pl;
2444 framepos_t internal_start;
2447 if ((pl = (*i)->playlist()) == 0) {
2451 if ((current = pl->top_region_at (start)) == 0) {
2455 internal_start = start - current->position();
2456 RegionFactory::region_name (new_name, current->name(), true);
2460 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2461 plist.add (ARDOUR::Properties::length, selection_cnt);
2462 plist.add (ARDOUR::Properties::name, new_name);
2463 plist.add (ARDOUR::Properties::layer, 0);
2465 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2470 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2472 if (selection->time.empty() || selection->tracks.empty()) {
2476 framepos_t start = selection->time[clicked_selection].start;
2477 framepos_t end = selection->time[clicked_selection].end;
2479 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2480 sort_track_selection (ts);
2482 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2483 boost::shared_ptr<Region> current;
2484 boost::shared_ptr<Playlist> playlist;
2485 framepos_t internal_start;
2488 if ((playlist = (*i)->playlist()) == 0) {
2492 if ((current = playlist->top_region_at(start)) == 0) {
2496 internal_start = start - current->position();
2497 RegionFactory::region_name (new_name, current->name(), true);
2501 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2502 plist.add (ARDOUR::Properties::length, end - start + 1);
2503 plist.add (ARDOUR::Properties::name, new_name);
2505 new_regions.push_back (RegionFactory::create (current, plist));
2510 Editor::split_multichannel_region ()
2512 RegionSelection rs = get_regions_from_selection_and_entered ();
2518 vector< boost::shared_ptr<Region> > v;
2520 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2521 (*x)->region()->separate_by_channel (*_session, v);
2526 Editor::new_region_from_selection ()
2528 region_from_selection ();
2529 cancel_selection ();
2533 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2535 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2536 case Evoral::OverlapNone:
2544 * - selected tracks, or if there are none...
2545 * - tracks containing selected regions, or if there are none...
2550 Editor::get_tracks_for_range_action () const
2554 if (selection->tracks.empty()) {
2556 /* use tracks with selected regions */
2558 RegionSelection rs = selection->regions;
2560 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2561 TimeAxisView* tv = &(*i)->get_time_axis_view();
2563 if (!t.contains (tv)) {
2569 /* no regions and no tracks: use all tracks */
2575 t = selection->tracks;
2578 return t.filter_to_unique_playlists();
2582 Editor::separate_regions_between (const TimeSelection& ts)
2584 bool in_command = false;
2585 boost::shared_ptr<Playlist> playlist;
2586 RegionSelection new_selection;
2588 TrackViewList tmptracks = get_tracks_for_range_action ();
2589 sort_track_selection (tmptracks);
2591 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2593 RouteTimeAxisView* rtv;
2595 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2597 if (rtv->is_track()) {
2599 /* no edits to destructive tracks */
2601 if (rtv->track()->destructive()) {
2605 if ((playlist = rtv->playlist()) != 0) {
2607 playlist->clear_changes ();
2609 /* XXX need to consider musical time selections here at some point */
2611 double speed = rtv->track()->speed();
2614 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2616 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2617 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2619 latest_regionviews.clear ();
2621 playlist->partition ((framepos_t)((*t).start * speed),
2622 (framepos_t)((*t).end * speed), false);
2626 if (!latest_regionviews.empty()) {
2628 rtv->view()->foreach_regionview (sigc::bind (
2629 sigc::ptr_fun (add_if_covered),
2630 &(*t), &new_selection));
2633 begin_reversible_command (_("separate"));
2637 /* pick up changes to existing regions */
2639 vector<Command*> cmds;
2640 playlist->rdiff (cmds);
2641 _session->add_commands (cmds);
2643 /* pick up changes to the playlist itself (adds/removes)
2646 _session->add_command(new StatefulDiffCommand (playlist));
2655 selection->set (new_selection);
2656 set_mouse_mode (MouseObject);
2658 commit_reversible_command ();
2662 struct PlaylistState {
2663 boost::shared_ptr<Playlist> playlist;
2667 /** Take tracks from get_tracks_for_range_action and cut any regions
2668 * on those tracks so that the tracks are empty over the time
2672 Editor::separate_region_from_selection ()
2674 /* preferentially use *all* ranges in the time selection if we're in range mode
2675 to allow discontiguous operation, since get_edit_op_range() currently
2676 returns a single range.
2679 if (mouse_mode == MouseRange && !selection->time.empty()) {
2681 separate_regions_between (selection->time);
2688 if (get_edit_op_range (start, end)) {
2690 AudioRange ar (start, end, 1);
2694 separate_regions_between (ts);
2700 Editor::separate_region_from_punch ()
2702 Location* loc = _session->locations()->auto_punch_location();
2704 separate_regions_using_location (*loc);
2709 Editor::separate_region_from_loop ()
2711 Location* loc = _session->locations()->auto_loop_location();
2713 separate_regions_using_location (*loc);
2718 Editor::separate_regions_using_location (Location& loc)
2720 if (loc.is_mark()) {
2724 AudioRange ar (loc.start(), loc.end(), 1);
2729 separate_regions_between (ts);
2732 /** Separate regions under the selected region */
2734 Editor::separate_under_selected_regions ()
2736 vector<PlaylistState> playlists;
2740 rs = get_regions_from_selection_and_entered();
2742 if (!_session || rs.empty()) {
2746 begin_reversible_command (_("separate region under"));
2748 list<boost::shared_ptr<Region> > regions_to_remove;
2750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2751 // we can't just remove the region(s) in this loop because
2752 // this removes them from the RegionSelection, and they thus
2753 // disappear from underneath the iterator, and the ++i above
2754 // SEGVs in a puzzling fashion.
2756 // so, first iterate over the regions to be removed from rs and
2757 // add them to the regions_to_remove list, and then
2758 // iterate over the list to actually remove them.
2760 regions_to_remove.push_back ((*i)->region());
2763 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2765 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2768 // is this check necessary?
2772 vector<PlaylistState>::iterator i;
2774 //only take state if this is a new playlist.
2775 for (i = playlists.begin(); i != playlists.end(); ++i) {
2776 if ((*i).playlist == playlist) {
2781 if (i == playlists.end()) {
2783 PlaylistState before;
2784 before.playlist = playlist;
2785 before.before = &playlist->get_state();
2787 playlist->freeze ();
2788 playlists.push_back(before);
2791 //Partition on the region bounds
2792 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2794 //Re-add region that was just removed due to the partition operation
2795 playlist->add_region( (*rl), (*rl)->first_frame() );
2798 vector<PlaylistState>::iterator pl;
2800 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2801 (*pl).playlist->thaw ();
2802 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2805 commit_reversible_command ();
2809 Editor::crop_region_to_selection ()
2811 if (!selection->time.empty()) {
2813 crop_region_to (selection->time.start(), selection->time.end_frame());
2820 if (get_edit_op_range (start, end)) {
2821 crop_region_to (start, end);
2828 Editor::crop_region_to (framepos_t start, framepos_t end)
2830 vector<boost::shared_ptr<Playlist> > playlists;
2831 boost::shared_ptr<Playlist> playlist;
2834 if (selection->tracks.empty()) {
2835 ts = track_views.filter_to_unique_playlists();
2837 ts = selection->tracks.filter_to_unique_playlists ();
2840 sort_track_selection (ts);
2842 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2844 RouteTimeAxisView* rtv;
2846 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2848 boost::shared_ptr<Track> t = rtv->track();
2850 if (t != 0 && ! t->destructive()) {
2852 if ((playlist = rtv->playlist()) != 0) {
2853 playlists.push_back (playlist);
2859 if (playlists.empty()) {
2863 framepos_t the_start;
2867 begin_reversible_command (_("trim to selection"));
2869 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2871 boost::shared_ptr<Region> region;
2875 if ((region = (*i)->top_region_at(the_start)) == 0) {
2879 /* now adjust lengths to that we do the right thing
2880 if the selection extends beyond the region
2883 the_start = max (the_start, (framepos_t) region->position());
2884 if (max_framepos - the_start < region->length()) {
2885 the_end = the_start + region->length() - 1;
2887 the_end = max_framepos;
2889 the_end = min (end, the_end);
2890 cnt = the_end - the_start + 1;
2892 region->clear_changes ();
2893 region->trim_to (the_start, cnt);
2894 _session->add_command (new StatefulDiffCommand (region));
2897 commit_reversible_command ();
2901 Editor::region_fill_track ()
2903 RegionSelection rs = get_regions_from_selection_and_entered ();
2905 if (!_session || rs.empty()) {
2909 framepos_t const end = _session->current_end_frame ();
2911 begin_reversible_command (Operations::region_fill);
2913 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2915 boost::shared_ptr<Region> region ((*i)->region());
2917 boost::shared_ptr<Playlist> pl = region->playlist();
2919 if (end <= region->last_frame()) {
2923 double times = (double) (end - region->last_frame()) / (double) region->length();
2929 pl->clear_changes ();
2930 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2931 _session->add_command (new StatefulDiffCommand (pl));
2934 commit_reversible_command ();
2938 Editor::region_fill_selection ()
2940 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2944 if (selection->time.empty()) {
2948 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2953 framepos_t start = selection->time[clicked_selection].start;
2954 framepos_t end = selection->time[clicked_selection].end;
2956 boost::shared_ptr<Playlist> playlist;
2958 if (selection->tracks.empty()) {
2962 framepos_t selection_length = end - start;
2963 float times = (float)selection_length / region->length();
2965 begin_reversible_command (Operations::fill_selection);
2967 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2969 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
2971 if ((playlist = (*i)->playlist()) == 0) {
2975 playlist->clear_changes ();
2976 playlist->add_region (RegionFactory::create (region, true), start, times);
2977 _session->add_command (new StatefulDiffCommand (playlist));
2980 commit_reversible_command ();
2984 Editor::set_region_sync_position ()
2986 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
2990 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
2992 bool in_command = false;
2994 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2996 if (!(*r)->region()->covers (where)) {
3000 boost::shared_ptr<Region> region ((*r)->region());
3003 begin_reversible_command (_("set sync point"));
3007 region->clear_changes ();
3008 region->set_sync_position (where);
3009 _session->add_command(new StatefulDiffCommand (region));
3013 commit_reversible_command ();
3017 /** Remove the sync positions of the selection */
3019 Editor::remove_region_sync ()
3021 RegionSelection rs = get_regions_from_selection_and_entered ();
3027 begin_reversible_command (_("remove region sync"));
3029 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3031 (*i)->region()->clear_changes ();
3032 (*i)->region()->clear_sync_position ();
3033 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3036 commit_reversible_command ();
3040 Editor::naturalize_region ()
3042 RegionSelection rs = get_regions_from_selection_and_entered ();
3048 if (rs.size() > 1) {
3049 begin_reversible_command (_("move regions to original position"));
3051 begin_reversible_command (_("move region to original position"));
3054 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3055 (*i)->region()->clear_changes ();
3056 (*i)->region()->move_to_natural_position ();
3057 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3060 commit_reversible_command ();
3064 Editor::align_regions (RegionPoint what)
3066 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3072 begin_reversible_command (_("align selection"));
3074 framepos_t const position = get_preferred_edit_position ();
3076 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3077 align_region_internal ((*i)->region(), what, position);
3080 commit_reversible_command ();
3083 struct RegionSortByTime {
3084 bool operator() (const RegionView* a, const RegionView* b) {
3085 return a->region()->position() < b->region()->position();
3090 Editor::align_regions_relative (RegionPoint point)
3092 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3098 framepos_t const position = get_preferred_edit_position ();
3100 framepos_t distance = 0;
3104 list<RegionView*> sorted;
3105 rs.by_position (sorted);
3107 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3112 if (position > r->position()) {
3113 distance = position - r->position();
3115 distance = r->position() - position;
3121 if (position > r->last_frame()) {
3122 distance = position - r->last_frame();
3123 pos = r->position() + distance;
3125 distance = r->last_frame() - position;
3126 pos = r->position() - distance;
3132 pos = r->adjust_to_sync (position);
3133 if (pos > r->position()) {
3134 distance = pos - r->position();
3136 distance = r->position() - pos;
3142 if (pos == r->position()) {
3146 begin_reversible_command (_("align selection (relative)"));
3148 /* move first one specially */
3150 r->clear_changes ();
3151 r->set_position (pos);
3152 _session->add_command(new StatefulDiffCommand (r));
3154 /* move rest by the same amount */
3158 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3160 boost::shared_ptr<Region> region ((*i)->region());
3162 region->clear_changes ();
3165 region->set_position (region->position() + distance);
3167 region->set_position (region->position() - distance);
3170 _session->add_command(new StatefulDiffCommand (region));
3174 commit_reversible_command ();
3178 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3180 begin_reversible_command (_("align region"));
3181 align_region_internal (region, point, position);
3182 commit_reversible_command ();
3186 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3188 region->clear_changes ();
3192 region->set_position (region->adjust_to_sync (position));
3196 if (position > region->length()) {
3197 region->set_position (position - region->length());
3202 region->set_position (position);
3206 _session->add_command(new StatefulDiffCommand (region));
3210 Editor::trim_region_front ()
3216 Editor::trim_region_back ()
3218 trim_region (false);
3222 Editor::trim_region (bool front)
3224 framepos_t where = get_preferred_edit_position();
3225 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3231 begin_reversible_command (front ? _("trim front") : _("trim back"));
3233 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3234 if (!(*i)->region()->locked()) {
3236 (*i)->region()->clear_changes ();
3239 (*i)->region()->trim_front (where);
3241 (*i)->region()->trim_end (where);
3244 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3248 commit_reversible_command ();
3251 /** Trim the end of the selected regions to the position of the edit cursor */
3253 Editor::trim_region_to_loop ()
3255 Location* loc = _session->locations()->auto_loop_location();
3259 trim_region_to_location (*loc, _("trim to loop"));
3263 Editor::trim_region_to_punch ()
3265 Location* loc = _session->locations()->auto_punch_location();
3269 trim_region_to_location (*loc, _("trim to punch"));
3273 Editor::trim_region_to_location (const Location& loc, const char* str)
3275 RegionSelection rs = get_regions_from_selection_and_entered ();
3277 begin_reversible_command (str);
3279 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3280 RegionView* rv = (*x);
3282 /* require region to span proposed trim */
3283 switch (rv->region()->coverage (loc.start(), loc.end())) {
3284 case Evoral::OverlapInternal:
3290 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3299 if (tav->track() != 0) {
3300 speed = tav->track()->speed();
3303 start = session_frame_to_track_frame (loc.start(), speed);
3304 end = session_frame_to_track_frame (loc.end(), speed);
3306 rv->region()->clear_changes ();
3307 rv->region()->trim_to (start, (end - start));
3308 _session->add_command(new StatefulDiffCommand (rv->region()));
3311 commit_reversible_command ();
3315 Editor::trim_region_to_previous_region_end ()
3317 return trim_to_region(false);
3321 Editor::trim_region_to_next_region_start ()
3323 return trim_to_region(true);
3327 Editor::trim_to_region(bool forward)
3329 RegionSelection rs = get_regions_from_selection_and_entered ();
3331 begin_reversible_command (_("trim to region"));
3333 boost::shared_ptr<Region> next_region;
3335 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3337 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3343 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3351 if (atav->track() != 0) {
3352 speed = atav->track()->speed();
3356 boost::shared_ptr<Region> region = arv->region();
3357 boost::shared_ptr<Playlist> playlist (region->playlist());
3359 region->clear_changes ();
3363 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3369 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3370 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3374 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3380 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3382 arv->region_changed (ARDOUR::bounds_change);
3385 _session->add_command(new StatefulDiffCommand (region));
3388 commit_reversible_command ();
3392 Editor::unfreeze_route ()
3394 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3398 clicked_routeview->track()->unfreeze ();
3402 Editor::_freeze_thread (void* arg)
3404 return static_cast<Editor*>(arg)->freeze_thread ();
3408 Editor::freeze_thread ()
3410 /* create event pool because we may need to talk to the session */
3411 SessionEvent::create_per_thread_pool ("freeze events", 64);
3412 /* create per-thread buffers for process() tree to use */
3413 current_interthread_info->process_thread.get_buffers ();
3414 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3415 current_interthread_info->done = true;
3416 current_interthread_info->process_thread.drop_buffers();
3421 Editor::freeze_route ()
3427 /* stop transport before we start. this is important */
3429 _session->request_transport_speed (0.0);
3431 /* wait for just a little while, because the above call is asynchronous */
3435 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3439 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3441 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3442 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3444 d.set_title (_("Cannot freeze"));
3449 if (clicked_routeview->track()->has_external_redirects()) {
3450 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"
3451 "Freezing will only process the signal as far as the first send/insert/return."),
3452 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3454 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3455 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3456 d.set_title (_("Freeze Limits"));
3458 int response = d.run ();
3461 case Gtk::RESPONSE_CANCEL:
3468 InterThreadInfo itt;
3469 current_interthread_info = &itt;
3471 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3473 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3475 set_canvas_cursor (_cursors->wait);
3477 while (!itt.done && !itt.cancel) {
3478 gtk_main_iteration ();
3481 current_interthread_info = 0;
3482 set_canvas_cursor (current_canvas_cursor);
3486 Editor::bounce_range_selection (bool replace, bool enable_processing)
3488 if (selection->time.empty()) {
3492 TrackSelection views = selection->tracks;
3494 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3496 if (enable_processing) {
3498 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3500 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3502 _("You can't perform this operation because the processing of the signal "
3503 "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3504 "You can do this without processing, which is a different operation.")
3506 d.set_title (_("Cannot bounce"));
3513 framepos_t start = selection->time[clicked_selection].start;
3514 framepos_t end = selection->time[clicked_selection].end;
3515 framepos_t cnt = end - start + 1;
3517 begin_reversible_command (_("bounce range"));
3519 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3521 RouteTimeAxisView* rtv;
3523 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3527 boost::shared_ptr<Playlist> playlist;
3529 if ((playlist = rtv->playlist()) == 0) {
3533 InterThreadInfo itt;
3535 playlist->clear_changes ();
3536 playlist->clear_owned_changes ();
3538 boost::shared_ptr<Region> r;
3540 if (enable_processing) {
3541 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3543 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3551 list<AudioRange> ranges;
3552 ranges.push_back (AudioRange (start, start+cnt, 0));
3553 playlist->cut (ranges); // discard result
3554 playlist->add_region (r, start);
3557 vector<Command*> cmds;
3558 playlist->rdiff (cmds);
3559 _session->add_commands (cmds);
3561 _session->add_command (new StatefulDiffCommand (playlist));
3564 commit_reversible_command ();
3567 /** Delete selected regions, automation points or a time range */
3574 /** Cut selected regions, automation points or a time range */
3581 /** Copy selected regions, automation points or a time range */
3589 /** @return true if a Cut, Copy or Clear is possible */
3591 Editor::can_cut_copy () const
3593 switch (current_mouse_mode()) {
3596 if (!selection->regions.empty() || !selection->points.empty()) {
3602 if (!selection->time.empty()) {
3615 /** Cut, copy or clear selected regions, automation points or a time range.
3616 * @param op Operation (Cut, Copy or Clear)
3619 Editor::cut_copy (CutCopyOp op)
3621 /* only cancel selection if cut/copy is successful.*/
3627 opname = _("delete");
3636 opname = _("clear");
3640 /* if we're deleting something, and the mouse is still pressed,
3641 the thing we started a drag for will be gone when we release
3642 the mouse button(s). avoid this. see part 2 at the end of
3646 if (op == Delete || op == Cut || op == Clear) {
3647 if (_drags->active ()) {
3652 cut_buffer->clear ();
3654 if (entered_marker) {
3656 /* cut/delete op while pointing at a marker */
3659 Location* loc = find_location_from_marker (entered_marker, ignored);
3661 if (_session && loc) {
3662 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3669 if (internal_editing()) {
3671 switch (current_mouse_mode()) {
3684 /* we only want to cut regions if some are selected */
3686 if (doing_object_stuff()) {
3687 rs = get_regions_from_selection ();
3688 if (!rs.empty() || !selection->points.empty()) {
3690 begin_reversible_command (opname + _(" objects"));
3693 cut_copy_regions (op, rs);
3695 if (op == Cut || op == Delete) {
3696 selection->clear_regions ();
3700 if (!selection->points.empty()) {
3701 cut_copy_points (op);
3703 if (op == Cut || op == Delete) {
3704 selection->clear_points ();
3707 commit_reversible_command ();
3710 if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3711 /* don't cause suprises */
3716 if (doing_range_stuff()) {
3717 if (selection->time.empty()) {
3718 framepos_t start, end;
3719 if (!get_edit_op_range (start, end)) {
3722 selection->set (start, end);
3725 begin_reversible_command (opname + _(" range"));
3726 cut_copy_ranges (op);
3727 commit_reversible_command ();
3729 if (op == Cut || op == Delete) {
3730 selection->clear_time ();
3736 if (op == Delete || op == Cut || op == Clear) {
3741 struct AutomationRecord {
3742 AutomationRecord () : state (0) {}
3743 AutomationRecord (XMLNode* s) : state (s) {}
3745 XMLNode* state; ///< state before any operation
3746 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3749 /** Cut, copy or clear selected automation points.
3750 * @param op Operation (Cut, Copy or Clear)
3753 Editor::cut_copy_points (CutCopyOp op)
3755 if (selection->points.empty ()) {
3759 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3760 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3762 /* Keep a record of the AutomationLists that we end up using in this operation */
3763 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3766 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3767 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3768 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3769 if (lists.find (al) == lists.end ()) {
3770 /* We haven't seen this list yet, so make a record for it. This includes
3771 taking a copy of its current state, in case this is needed for undo later.
3773 lists[al] = AutomationRecord (&al->get_state ());
3777 if (op == Cut || op == Copy) {
3778 /* This operation will involve putting things in the cut buffer, so create an empty
3779 ControlList for each of our source lists to put the cut buffer data in.
3781 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3782 i->second.copy = i->first->create (i->first->parameter ());
3785 /* Add all selected points to the relevant copy ControlLists */
3786 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3787 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3788 AutomationList::const_iterator j = (*i)->model ();
3789 lists[al].copy->add ((*j)->when, (*j)->value);
3792 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3793 /* Correct this copy list so that it starts at time 0 */
3794 double const start = i->second.copy->front()->when;
3795 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3796 (*j)->when -= start;
3799 /* And add it to the cut buffer */
3800 cut_buffer->add (i->second.copy);
3804 if (op == Delete || op == Cut) {
3805 /* This operation needs to remove things from the main AutomationList, so do that now */
3807 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3808 i->first->freeze ();
3811 /* Remove each selected point from its AutomationList */
3812 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3813 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3814 al->erase ((*i)->model ());
3817 /* Thaw the lists and add undo records for them */
3818 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3819 boost::shared_ptr<AutomationList> al = i->first;
3821 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3826 /** Cut, copy or clear selected automation points.
3827 * @param op Operation (Cut, Copy or Clear)
3830 Editor::cut_copy_midi (CutCopyOp op)
3832 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3833 MidiRegionView* mrv = *i;
3834 mrv->cut_copy_clear (op);
3840 struct lt_playlist {
3841 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3842 return a.playlist < b.playlist;
3846 struct PlaylistMapping {
3848 boost::shared_ptr<Playlist> pl;
3850 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3853 /** Remove `clicked_regionview' */
3855 Editor::remove_clicked_region ()
3857 if (clicked_routeview == 0 || clicked_regionview == 0) {
3861 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3863 begin_reversible_command (_("remove region"));
3864 playlist->clear_changes ();
3865 playlist->clear_owned_changes ();
3866 playlist->remove_region (clicked_regionview->region());
3868 /* We might have removed regions, which alters other regions' layering_index,
3869 so we need to do a recursive diff here.
3871 vector<Command*> cmds;
3872 playlist->rdiff (cmds);
3873 _session->add_commands (cmds);
3875 _session->add_command(new StatefulDiffCommand (playlist));
3876 commit_reversible_command ();
3880 /** Remove the selected regions */
3882 Editor::remove_selected_regions ()
3884 RegionSelection rs = get_regions_from_selection_and_entered ();
3886 if (!_session || rs.empty()) {
3890 begin_reversible_command (_("remove region"));
3892 list<boost::shared_ptr<Region> > regions_to_remove;
3894 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3895 // we can't just remove the region(s) in this loop because
3896 // this removes them from the RegionSelection, and they thus
3897 // disappear from underneath the iterator, and the ++i above
3898 // SEGVs in a puzzling fashion.
3900 // so, first iterate over the regions to be removed from rs and
3901 // add them to the regions_to_remove list, and then
3902 // iterate over the list to actually remove them.
3904 regions_to_remove.push_back ((*i)->region());
3907 vector<boost::shared_ptr<Playlist> > playlists;
3909 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3911 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3914 // is this check necessary?
3918 /* get_regions_from_selection_and_entered() guarantees that
3919 the playlists involved are unique, so there is no need
3923 playlists.push_back (playlist);
3925 playlist->clear_changes ();
3926 playlist->clear_owned_changes ();
3927 playlist->freeze ();
3928 playlist->remove_region (*rl);
3931 vector<boost::shared_ptr<Playlist> >::iterator pl;
3933 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3936 /* We might have removed regions, which alters other regions' layering_index,
3937 so we need to do a recursive diff here.
3939 vector<Command*> cmds;
3940 (*pl)->rdiff (cmds);
3941 _session->add_commands (cmds);
3943 _session->add_command(new StatefulDiffCommand (*pl));
3946 commit_reversible_command ();
3949 /** Cut, copy or clear selected regions.
3950 * @param op Operation (Cut, Copy or Clear)
3953 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3955 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3956 a map when we want ordered access to both elements. i think.
3959 vector<PlaylistMapping> pmap;
3961 framepos_t first_position = max_framepos;
3963 typedef set<boost::shared_ptr<Playlist> > FreezeList;
3964 FreezeList freezelist;
3966 /* get ordering correct before we cut/copy */
3968 rs.sort_by_position_and_track ();
3970 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3972 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3974 if (op == Cut || op == Clear || op == Delete) {
3975 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3978 FreezeList::iterator fl;
3980 // only take state if this is a new playlist.
3981 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3987 if (fl == freezelist.end()) {
3988 pl->clear_changes();
3989 pl->clear_owned_changes ();
3991 freezelist.insert (pl);
3996 TimeAxisView* tv = &(*x)->get_time_axis_view();
3997 vector<PlaylistMapping>::iterator z;
3999 for (z = pmap.begin(); z != pmap.end(); ++z) {
4000 if ((*z).tv == tv) {
4005 if (z == pmap.end()) {
4006 pmap.push_back (PlaylistMapping (tv));
4010 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4012 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4015 /* region not yet associated with a playlist (e.g. unfinished
4022 TimeAxisView& tv = (*x)->get_time_axis_view();
4023 boost::shared_ptr<Playlist> npl;
4024 RegionSelection::iterator tmp;
4031 vector<PlaylistMapping>::iterator z;
4033 for (z = pmap.begin(); z != pmap.end(); ++z) {
4034 if ((*z).tv == &tv) {
4039 assert (z != pmap.end());
4042 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4050 boost::shared_ptr<Region> r = (*x)->region();
4051 boost::shared_ptr<Region> _xx;
4057 pl->remove_region (r);
4061 _xx = RegionFactory::create (r);
4062 npl->add_region (_xx, r->position() - first_position);
4063 pl->remove_region (r);
4067 /* copy region before adding, so we're not putting same object into two different playlists */
4068 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4072 pl->remove_region (r);
4081 list<boost::shared_ptr<Playlist> > foo;
4083 /* the pmap is in the same order as the tracks in which selected regions occured */
4085 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4088 foo.push_back ((*i).pl);
4093 cut_buffer->set (foo);
4097 _last_cut_copy_source_track = 0;
4099 _last_cut_copy_source_track = pmap.front().tv;
4103 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4106 /* We might have removed regions, which alters other regions' layering_index,
4107 so we need to do a recursive diff here.
4109 vector<Command*> cmds;
4110 (*pl)->rdiff (cmds);
4111 _session->add_commands (cmds);
4113 _session->add_command (new StatefulDiffCommand (*pl));
4118 Editor::cut_copy_ranges (CutCopyOp op)
4120 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4122 /* Sort the track selection now, so that it if is used, the playlists
4123 selected by the calls below to cut_copy_clear are in the order that
4124 their tracks appear in the editor. This makes things like paste
4125 of ranges work properly.
4128 sort_track_selection (ts);
4131 if (!entered_track) {
4134 ts.push_back (entered_track);
4137 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4138 (*i)->cut_copy_clear (*selection, op);
4143 Editor::paste (float times, bool from_context)
4145 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4147 paste_internal (get_preferred_edit_position (false, from_context), times);
4151 Editor::mouse_paste ()
4156 if (!mouse_frame (where, ignored)) {
4161 paste_internal (where, 1);
4165 Editor::paste_internal (framepos_t position, float times)
4167 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4169 if (internal_editing()) {
4170 if (cut_buffer->midi_notes.empty()) {
4174 if (cut_buffer->empty()) {
4179 if (position == max_framepos) {
4180 position = get_preferred_edit_position();
4181 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4185 TrackViewList::iterator i;
4188 /* get everything in the correct order */
4190 if (_edit_point == Editing::EditAtMouse && entered_track) {
4191 /* With the mouse edit point, paste onto the track under the mouse */
4192 ts.push_back (entered_track);
4193 } else if (!selection->tracks.empty()) {
4194 /* Otherwise, if there are some selected tracks, paste to them */
4195 ts = selection->tracks.filter_to_unique_playlists ();
4196 sort_track_selection (ts);
4197 } else if (_last_cut_copy_source_track) {
4198 /* Otherwise paste to the track that the cut/copy came from;
4199 see discussion in mantis #3333.
4201 ts.push_back (_last_cut_copy_source_track);
4204 if (internal_editing ()) {
4206 /* undo/redo is handled by individual tracks/regions */
4208 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4211 RegionSelection::iterator r;
4212 MidiNoteSelection::iterator cb;
4214 get_regions_at (rs, position, ts);
4216 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4217 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4218 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4220 mrv->paste (position, times, **cb);
4228 /* we do redo (do you do voodoo?) */
4230 begin_reversible_command (Operations::paste);
4232 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4233 (*i)->paste (position, times, *cut_buffer, nth);
4236 commit_reversible_command ();
4241 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4243 boost::shared_ptr<Playlist> playlist;
4244 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4245 RegionSelection foo;
4247 framepos_t const start_frame = regions.start ();
4248 framepos_t const end_frame = regions.end_frame ();
4250 begin_reversible_command (Operations::duplicate_region);
4252 selection->clear_regions ();
4254 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4256 boost::shared_ptr<Region> r ((*i)->region());
4258 TimeAxisView& tv = (*i)->get_time_axis_view();
4259 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4260 latest_regionviews.clear ();
4261 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4263 playlist = (*i)->region()->playlist();
4264 playlist->clear_changes ();
4265 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4266 _session->add_command(new StatefulDiffCommand (playlist));
4270 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4273 commit_reversible_command ();
4276 selection->set (foo);
4281 Editor::duplicate_selection (float times)
4283 if (selection->time.empty() || selection->tracks.empty()) {
4287 boost::shared_ptr<Playlist> playlist;
4288 vector<boost::shared_ptr<Region> > new_regions;
4289 vector<boost::shared_ptr<Region> >::iterator ri;
4291 create_region_from_selection (new_regions);
4293 if (new_regions.empty()) {
4297 begin_reversible_command (_("duplicate selection"));
4299 ri = new_regions.begin();
4301 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4303 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4304 if ((playlist = (*i)->playlist()) == 0) {
4307 playlist->clear_changes ();
4308 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4309 _session->add_command (new StatefulDiffCommand (playlist));
4312 if (ri == new_regions.end()) {
4317 commit_reversible_command ();
4320 /** Reset all selected points to the relevant default value */
4322 Editor::reset_point_selection ()
4324 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4325 ARDOUR::AutomationList::iterator j = (*i)->model ();
4326 (*j)->value = (*i)->line().the_list()->default_value ();
4331 Editor::center_playhead ()
4333 float page = _canvas_width * frames_per_unit;
4334 center_screen_internal (playhead_cursor->current_frame, page);
4338 Editor::center_edit_point ()
4340 float page = _canvas_width * frames_per_unit;
4341 center_screen_internal (get_preferred_edit_position(), page);
4344 /** Caller must begin and commit a reversible command */
4346 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4348 playlist->clear_changes ();
4350 _session->add_command (new StatefulDiffCommand (playlist));
4354 Editor::nudge_track (bool use_edit, bool forwards)
4356 boost::shared_ptr<Playlist> playlist;
4357 framepos_t distance;
4358 framepos_t next_distance;
4362 start = get_preferred_edit_position();
4367 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4371 if (selection->tracks.empty()) {
4375 begin_reversible_command (_("nudge track"));
4377 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4379 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4381 if ((playlist = (*i)->playlist()) == 0) {
4385 playlist->clear_changes ();
4386 playlist->clear_owned_changes ();
4388 playlist->nudge_after (start, distance, forwards);
4390 vector<Command*> cmds;
4392 playlist->rdiff (cmds);
4393 _session->add_commands (cmds);
4395 _session->add_command (new StatefulDiffCommand (playlist));
4398 commit_reversible_command ();
4402 Editor::remove_last_capture ()
4404 vector<string> choices;
4411 if (Config->get_verify_remove_last_capture()) {
4412 prompt = _("Do you really want to destroy the last capture?"
4413 "\n(This is destructive and cannot be undone)");
4415 choices.push_back (_("No, do nothing."));
4416 choices.push_back (_("Yes, destroy it."));
4418 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4420 if (prompter.run () == 1) {
4421 _session->remove_last_capture ();
4422 _regions->redisplay ();
4426 _session->remove_last_capture();
4427 _regions->redisplay ();
4432 Editor::normalize_region ()
4438 RegionSelection rs = get_regions_from_selection_and_entered ();
4444 NormalizeDialog dialog (rs.size() > 1);
4446 if (dialog.run () == RESPONSE_CANCEL) {
4450 set_canvas_cursor (_cursors->wait);
4453 /* XXX: should really only count audio regions here */
4454 int const regions = rs.size ();
4456 /* Make a list of the selected audio regions' maximum amplitudes, and also
4457 obtain the maximum amplitude of them all.
4459 list<double> max_amps;
4461 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4462 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4464 dialog.descend (1.0 / regions);
4465 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4468 /* the user cancelled the operation */
4469 set_canvas_cursor (current_canvas_cursor);
4473 max_amps.push_back (a);
4474 max_amp = max (max_amp, a);
4479 begin_reversible_command (_("normalize"));
4481 list<double>::const_iterator a = max_amps.begin ();
4483 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4484 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4489 arv->region()->clear_changes ();
4491 double const amp = dialog.normalize_individually() ? *a : max_amp;
4493 arv->audio_region()->normalize (amp, dialog.target ());
4494 _session->add_command (new StatefulDiffCommand (arv->region()));
4499 commit_reversible_command ();
4500 set_canvas_cursor (current_canvas_cursor);
4505 Editor::reset_region_scale_amplitude ()
4511 RegionSelection rs = get_regions_from_selection_and_entered ();
4517 begin_reversible_command ("reset gain");
4519 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4520 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4523 arv->region()->clear_changes ();
4524 arv->audio_region()->set_scale_amplitude (1.0f);
4525 _session->add_command (new StatefulDiffCommand (arv->region()));
4528 commit_reversible_command ();
4532 Editor::adjust_region_gain (bool up)
4534 RegionSelection rs = get_regions_from_selection_and_entered ();
4536 if (!_session || rs.empty()) {
4540 begin_reversible_command ("adjust region gain");
4542 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4543 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4548 arv->region()->clear_changes ();
4550 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4558 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4559 _session->add_command (new StatefulDiffCommand (arv->region()));
4562 commit_reversible_command ();
4567 Editor::reverse_region ()
4573 Reverse rev (*_session);
4574 apply_filter (rev, _("reverse regions"));
4578 Editor::strip_region_silence ()
4584 RegionSelection rs = get_regions_from_selection_and_entered ();
4590 std::list<RegionView*> audio_only;
4592 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4593 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4595 audio_only.push_back (arv);
4599 StripSilenceDialog d (_session, audio_only);
4600 int const r = d.run ();
4604 if (r == Gtk::RESPONSE_OK) {
4605 ARDOUR::AudioIntervalMap silences;
4606 d.silences (silences);
4607 StripSilence s (*_session, silences, d.fade_length());
4608 apply_filter (s, _("strip silence"), &d);
4613 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4615 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4616 mrv.selection_as_notelist (selected, true);
4618 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4619 v.push_back (selected);
4621 framepos_t pos_frames = mrv.midi_region()->position();
4622 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4624 return op (mrv.midi_region()->model(), pos_beats, v);
4628 Editor::apply_midi_note_edit_op (MidiOperator& op)
4632 RegionSelection rs = get_regions_from_selection_and_entered ();
4638 begin_reversible_command (op.name ());
4640 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4641 RegionSelection::iterator tmp = r;
4644 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4647 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4650 _session->add_command (cmd);
4657 commit_reversible_command ();
4661 Editor::fork_region ()
4663 RegionSelection rs = get_regions_from_selection_and_entered ();
4669 begin_reversible_command (_("Fork Region(s)"));
4671 set_canvas_cursor (_cursors->wait);
4674 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4675 RegionSelection::iterator tmp = r;
4678 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4681 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4682 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4684 playlist->clear_changes ();
4685 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4686 _session->add_command(new StatefulDiffCommand (playlist));
4692 commit_reversible_command ();
4694 set_canvas_cursor (current_canvas_cursor);
4698 Editor::quantize_region ()
4700 int selected_midi_region_cnt = 0;
4706 RegionSelection rs = get_regions_from_selection_and_entered ();
4712 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4713 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4715 selected_midi_region_cnt++;
4719 if (selected_midi_region_cnt == 0) {
4723 QuantizeDialog* qd = new QuantizeDialog (*this);
4726 const int r = qd->run ();
4729 if (r == Gtk::RESPONSE_OK) {
4730 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4731 qd->start_grid_size(), qd->end_grid_size(),
4732 qd->strength(), qd->swing(), qd->threshold());
4734 apply_midi_note_edit_op (quant);
4739 Editor::insert_patch_change (bool from_context)
4741 RegionSelection rs = get_regions_from_selection_and_entered ();
4747 const framepos_t p = get_preferred_edit_position (false, from_context);
4749 cerr << "Got " << rs.size() << " regions to add patch change to\n";
4751 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4752 there may be more than one, but the PatchChangeDialog can only offer
4753 one set of patch menus.
4755 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4757 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4758 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4760 if (d.run() == RESPONSE_CANCEL) {
4764 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4765 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4767 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4768 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4775 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4777 RegionSelection rs = get_regions_from_selection_and_entered ();
4783 begin_reversible_command (command);
4785 set_canvas_cursor (_cursors->wait);
4789 int const N = rs.size ();
4791 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4792 RegionSelection::iterator tmp = r;
4795 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4797 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4800 progress->descend (1.0 / N);
4803 if (arv->audio_region()->apply (filter, progress) == 0) {
4805 playlist->clear_changes ();
4806 playlist->clear_owned_changes ();
4808 if (filter.results.empty ()) {
4810 /* no regions returned; remove the old one */
4811 playlist->remove_region (arv->region ());
4815 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4817 /* first region replaces the old one */
4818 playlist->replace_region (arv->region(), *res, (*res)->position());
4822 while (res != filter.results.end()) {
4823 playlist->add_region (*res, (*res)->position());
4829 /* We might have removed regions, which alters other regions' layering_index,
4830 so we need to do a recursive diff here.
4832 vector<Command*> cmds;
4833 playlist->rdiff (cmds);
4834 _session->add_commands (cmds);
4836 _session->add_command(new StatefulDiffCommand (playlist));
4842 progress->ascend ();
4850 commit_reversible_command ();
4853 set_canvas_cursor (current_canvas_cursor);
4857 Editor::external_edit_region ()
4863 Editor::reset_region_gain_envelopes ()
4865 RegionSelection rs = get_regions_from_selection_and_entered ();
4867 if (!_session || rs.empty()) {
4871 _session->begin_reversible_command (_("reset region gain"));
4873 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4874 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4876 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4877 XMLNode& before (alist->get_state());
4879 arv->audio_region()->set_default_envelope ();
4880 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4884 _session->commit_reversible_command ();
4888 Editor::set_region_gain_visibility (RegionView* rv)
4890 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4892 arv->update_envelope_visibility();
4897 Editor::set_gain_envelope_visibility ()
4903 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4904 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4906 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4912 Editor::toggle_gain_envelope_active ()
4914 if (_ignore_region_action) {
4918 RegionSelection rs = get_regions_from_selection_and_entered ();
4920 if (!_session || rs.empty()) {
4924 _session->begin_reversible_command (_("region gain envelope active"));
4926 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4927 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4929 arv->region()->clear_changes ();
4930 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4931 _session->add_command (new StatefulDiffCommand (arv->region()));
4935 _session->commit_reversible_command ();
4939 Editor::toggle_region_lock ()
4941 if (_ignore_region_action) {
4945 RegionSelection rs = get_regions_from_selection_and_entered ();
4947 if (!_session || rs.empty()) {
4951 _session->begin_reversible_command (_("toggle region lock"));
4953 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4954 (*i)->region()->clear_changes ();
4955 (*i)->region()->set_locked (!(*i)->region()->locked());
4956 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4959 _session->commit_reversible_command ();
4963 Editor::toggle_region_lock_style ()
4965 if (_ignore_region_action) {
4969 RegionSelection rs = get_regions_from_selection_and_entered ();
4971 if (!_session || rs.empty()) {
4975 _session->begin_reversible_command (_("region lock style"));
4977 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4978 (*i)->region()->clear_changes ();
4979 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4980 (*i)->region()->set_position_lock_style (ns);
4981 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4984 _session->commit_reversible_command ();
4988 Editor::toggle_opaque_region ()
4990 if (_ignore_region_action) {
4994 RegionSelection rs = get_regions_from_selection_and_entered ();
4996 if (!_session || rs.empty()) {
5000 _session->begin_reversible_command (_("change region opacity"));
5002 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5003 (*i)->region()->clear_changes ();
5004 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5005 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5008 _session->commit_reversible_command ();
5012 Editor::toggle_record_enable ()
5014 bool new_state = false;
5016 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5017 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5020 if (!rtav->is_track())
5024 new_state = !rtav->track()->record_enabled();
5028 rtav->track()->set_record_enabled (new_state, this);
5033 Editor::toggle_solo ()
5035 bool new_state = false;
5037 boost::shared_ptr<RouteList> rl (new RouteList);
5039 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5040 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5047 new_state = !rtav->route()->soloed ();
5051 rl->push_back (rtav->route());
5054 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5058 Editor::toggle_mute ()
5060 bool new_state = false;
5062 boost::shared_ptr<RouteList> rl (new RouteList);
5064 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5065 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5072 new_state = !rtav->route()->muted();
5076 rl->push_back (rtav->route());
5079 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5083 Editor::toggle_solo_isolate ()
5088 Editor::set_fade_length (bool in)
5090 RegionSelection rs = get_regions_from_selection_and_entered ();
5096 /* we need a region to measure the offset from the start */
5098 RegionView* rv = rs.front ();
5100 framepos_t pos = get_preferred_edit_position();
5104 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5105 /* edit point is outside the relevant region */
5110 if (pos <= rv->region()->position()) {
5114 len = pos - rv->region()->position();
5115 cmd = _("set fade in length");
5117 if (pos >= rv->region()->last_frame()) {
5121 len = rv->region()->last_frame() - pos;
5122 cmd = _("set fade out length");
5125 begin_reversible_command (cmd);
5127 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5128 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5134 boost::shared_ptr<AutomationList> alist;
5136 alist = tmp->audio_region()->fade_in();
5138 alist = tmp->audio_region()->fade_out();
5141 XMLNode &before = alist->get_state();
5144 tmp->audio_region()->set_fade_in_length (len);
5145 tmp->audio_region()->set_fade_in_active (true);
5147 tmp->audio_region()->set_fade_out_length (len);
5148 tmp->audio_region()->set_fade_out_active (true);
5151 XMLNode &after = alist->get_state();
5152 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5155 commit_reversible_command ();
5159 Editor::set_fade_in_shape (FadeShape shape)
5161 RegionSelection rs = get_regions_from_selection_and_entered ();
5167 begin_reversible_command (_("set fade in shape"));
5169 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5170 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5176 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5177 XMLNode &before = alist->get_state();
5179 tmp->audio_region()->set_fade_in_shape (shape);
5181 XMLNode &after = alist->get_state();
5182 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5185 commit_reversible_command ();
5190 Editor::set_fade_out_shape (FadeShape shape)
5192 RegionSelection rs = get_regions_from_selection_and_entered ();
5198 begin_reversible_command (_("set fade out shape"));
5200 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5201 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5207 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5208 XMLNode &before = alist->get_state();
5210 tmp->audio_region()->set_fade_out_shape (shape);
5212 XMLNode &after = alist->get_state();
5213 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5216 commit_reversible_command ();
5220 Editor::set_fade_in_active (bool yn)
5222 RegionSelection rs = get_regions_from_selection_and_entered ();
5228 begin_reversible_command (_("set fade in active"));
5230 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5231 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5238 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5240 ar->clear_changes ();
5241 ar->set_fade_in_active (yn);
5242 _session->add_command (new StatefulDiffCommand (ar));
5245 commit_reversible_command ();
5249 Editor::set_fade_out_active (bool yn)
5251 RegionSelection rs = get_regions_from_selection_and_entered ();
5257 begin_reversible_command (_("set fade out active"));
5259 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5260 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5266 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5268 ar->clear_changes ();
5269 ar->set_fade_out_active (yn);
5270 _session->add_command(new StatefulDiffCommand (ar));
5273 commit_reversible_command ();
5277 Editor::toggle_region_fades (int dir)
5279 if (_ignore_region_action) {
5283 boost::shared_ptr<AudioRegion> ar;
5286 RegionSelection rs = get_regions_from_selection_and_entered ();
5292 RegionSelection::iterator i;
5293 for (i = rs.begin(); i != rs.end(); ++i) {
5294 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5296 yn = ar->fade_out_active ();
5298 yn = ar->fade_in_active ();
5304 if (i == rs.end()) {
5308 /* XXX should this undo-able? */
5310 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5311 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5314 if (dir == 1 || dir == 0) {
5315 ar->set_fade_in_active (!yn);
5318 if (dir == -1 || dir == 0) {
5319 ar->set_fade_out_active (!yn);
5325 /** Update region fade visibility after its configuration has been changed */
5327 Editor::update_region_fade_visibility ()
5329 bool _fade_visibility = _session->config.get_show_region_fades ();
5331 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5332 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5334 if (_fade_visibility) {
5335 v->audio_view()->show_all_fades ();
5337 v->audio_view()->hide_all_fades ();
5344 Editor::set_edit_point ()
5349 if (!mouse_frame (where, ignored)) {
5355 if (selection->markers.empty()) {
5357 mouse_add_new_marker (where);
5362 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5365 loc->move_to (where);
5371 Editor::set_playhead_cursor ()
5373 if (entered_marker) {
5374 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5379 if (!mouse_frame (where, ignored)) {
5386 _session->request_locate (where, _session->transport_rolling());
5392 Editor::split_region ()
5394 if (((mouse_mode == MouseRange) ||
5395 (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5396 !selection->time.empty()) {
5397 separate_regions_between (selection->time);
5401 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5403 framepos_t where = get_preferred_edit_position ();
5409 split_regions_at (where, rs);
5412 struct EditorOrderRouteSorter {
5413 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5414 return a->order_key (EditorSort) < b->order_key (EditorSort);
5419 Editor::select_next_route()
5421 if (selection->tracks.empty()) {
5422 selection->set (track_views.front());
5426 TimeAxisView* current = selection->tracks.front();
5430 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5431 if (*i == current) {
5433 if (i != track_views.end()) {
5436 current = (*(track_views.begin()));
5437 //selection->set (*(track_views.begin()));
5442 rui = dynamic_cast<RouteUI *>(current);
5443 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5445 selection->set(current);
5447 ensure_track_visible(current);
5451 Editor::select_prev_route()
5453 if (selection->tracks.empty()) {
5454 selection->set (track_views.front());
5458 TimeAxisView* current = selection->tracks.front();
5462 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5463 if (*i == current) {
5465 if (i != track_views.rend()) {
5468 current = *(track_views.rbegin());
5473 rui = dynamic_cast<RouteUI *>(current);
5474 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5476 selection->set (current);
5478 ensure_track_visible(current);
5482 Editor::ensure_track_visible(TimeAxisView *track)
5484 if (track->hidden())
5487 double const current_view_min_y = vertical_adjustment.get_value();
5488 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5490 double const track_min_y = track->y_position ();
5491 double const track_max_y = track->y_position () + track->effective_height ();
5493 if (track_min_y >= current_view_min_y &&
5494 track_max_y <= current_view_max_y) {
5500 if (track_min_y < current_view_min_y) {
5501 // Track is above the current view
5502 new_value = track_min_y;
5504 // Track is below the current view
5505 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5508 vertical_adjustment.set_value(new_value);
5512 Editor::set_loop_from_selection (bool play)
5514 if (_session == 0 || selection->time.empty()) {
5518 framepos_t start = selection->time[clicked_selection].start;
5519 framepos_t end = selection->time[clicked_selection].end;
5521 set_loop_range (start, end, _("set loop range from selection"));
5524 _session->request_play_loop (true);
5525 _session->request_locate (start, true);
5530 Editor::set_loop_from_edit_range (bool play)
5532 if (_session == 0) {
5539 if (!get_edit_op_range (start, end)) {
5543 set_loop_range (start, end, _("set loop range from edit range"));
5546 _session->request_play_loop (true);
5547 _session->request_locate (start, true);
5552 Editor::set_loop_from_region (bool play)
5554 framepos_t start = max_framepos;
5557 RegionSelection rs = get_regions_from_selection_and_entered ();
5563 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5564 if ((*i)->region()->position() < start) {
5565 start = (*i)->region()->position();
5567 if ((*i)->region()->last_frame() + 1 > end) {
5568 end = (*i)->region()->last_frame() + 1;
5572 set_loop_range (start, end, _("set loop range from region"));
5575 _session->request_play_loop (true);
5576 _session->request_locate (start, true);
5581 Editor::set_punch_from_selection ()
5583 if (_session == 0 || selection->time.empty()) {
5587 framepos_t start = selection->time[clicked_selection].start;
5588 framepos_t end = selection->time[clicked_selection].end;
5590 set_punch_range (start, end, _("set punch range from selection"));
5594 Editor::set_punch_from_edit_range ()
5596 if (_session == 0) {
5603 if (!get_edit_op_range (start, end)) {
5607 set_punch_range (start, end, _("set punch range from edit range"));
5611 Editor::set_punch_from_region ()
5613 framepos_t start = max_framepos;
5616 RegionSelection rs = get_regions_from_selection_and_entered ();
5622 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5623 if ((*i)->region()->position() < start) {
5624 start = (*i)->region()->position();
5626 if ((*i)->region()->last_frame() + 1 > end) {
5627 end = (*i)->region()->last_frame() + 1;
5631 set_punch_range (start, end, _("set punch range from region"));
5635 Editor::pitch_shift_region ()
5637 RegionSelection rs = get_regions_from_selection_and_entered ();
5639 RegionSelection audio_rs;
5640 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5641 if (dynamic_cast<AudioRegionView*> (*i)) {
5642 audio_rs.push_back (*i);
5646 if (audio_rs.empty()) {
5650 pitch_shift (audio_rs, 1.2);
5654 Editor::transpose_region ()
5656 RegionSelection rs = get_regions_from_selection_and_entered ();
5658 list<MidiRegionView*> midi_region_views;
5659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5660 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5662 midi_region_views.push_back (mrv);
5667 int const r = d.run ();
5668 if (r != RESPONSE_ACCEPT) {
5672 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5673 (*i)->midi_region()->transpose (d.semitones ());
5678 Editor::set_tempo_from_region ()
5680 RegionSelection rs = get_regions_from_selection_and_entered ();
5682 if (!_session || rs.empty()) {
5686 RegionView* rv = rs.front();
5688 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5692 Editor::use_range_as_bar ()
5694 framepos_t start, end;
5695 if (get_edit_op_range (start, end)) {
5696 define_one_bar (start, end);
5701 Editor::define_one_bar (framepos_t start, framepos_t end)
5703 framepos_t length = end - start;
5705 const Meter& m (_session->tempo_map().meter_at (start));
5707 /* length = 1 bar */
5709 /* now we want frames per beat.
5710 we have frames per bar, and beats per bar, so ...
5713 /* XXXX METER MATH */
5715 double frames_per_beat = length / m.divisions_per_bar();
5717 /* beats per minute = */
5719 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5721 /* now decide whether to:
5723 (a) set global tempo
5724 (b) add a new tempo marker
5728 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5730 bool do_global = false;
5732 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5734 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5735 at the start, or create a new marker
5738 vector<string> options;
5739 options.push_back (_("Cancel"));
5740 options.push_back (_("Add new marker"));
5741 options.push_back (_("Set global tempo"));
5744 _("Define one bar"),
5745 _("Do you want to set the global tempo or add a new tempo marker?"),
5749 c.set_default_response (2);
5765 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5766 if the marker is at the region starter, change it, otherwise add
5771 begin_reversible_command (_("set tempo from region"));
5772 XMLNode& before (_session->tempo_map().get_state());
5775 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5776 } else if (t.frame() == start) {
5777 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5779 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5782 XMLNode& after (_session->tempo_map().get_state());
5784 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5785 commit_reversible_command ();
5789 Editor::split_region_at_transients ()
5791 AnalysisFeatureList positions;
5793 RegionSelection rs = get_regions_from_selection_and_entered ();
5795 if (!_session || rs.empty()) {
5799 _session->begin_reversible_command (_("split regions"));
5801 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5803 RegionSelection::iterator tmp;
5808 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5810 if (ar && (ar->get_transients (positions) == 0)) {
5811 split_region_at_points ((*i)->region(), positions, true);
5818 _session->commit_reversible_command ();
5823 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5825 bool use_rhythmic_rodent = false;
5827 boost::shared_ptr<Playlist> pl = r->playlist();
5829 list<boost::shared_ptr<Region> > new_regions;
5835 if (positions.empty()) {
5840 if (positions.size() > 20 && can_ferret) {
5841 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);
5842 MessageDialog msg (msgstr,
5845 Gtk::BUTTONS_OK_CANCEL);
5848 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5849 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5851 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5854 msg.set_title (_("Excessive split?"));
5857 int response = msg.run();
5863 case RESPONSE_APPLY:
5864 use_rhythmic_rodent = true;
5871 if (use_rhythmic_rodent) {
5872 show_rhythm_ferret ();
5876 AnalysisFeatureList::const_iterator x;
5878 pl->clear_changes ();
5879 pl->clear_owned_changes ();
5881 x = positions.begin();
5883 if (x == positions.end()) {
5888 pl->remove_region (r);
5892 while (x != positions.end()) {
5894 /* deal with positons that are out of scope of present region bounds */
5895 if (*x <= 0 || *x > r->length()) {
5900 /* file start = original start + how far we from the initial position ?
5903 framepos_t file_start = r->start() + pos;
5905 /* length = next position - current position
5908 framepos_t len = (*x) - pos;
5910 /* XXX we do we really want to allow even single-sample regions?
5911 shouldn't we have some kind of lower limit on region size?
5920 if (RegionFactory::region_name (new_name, r->name())) {
5924 /* do NOT announce new regions 1 by one, just wait till they are all done */
5928 plist.add (ARDOUR::Properties::start, file_start);
5929 plist.add (ARDOUR::Properties::length, len);
5930 plist.add (ARDOUR::Properties::name, new_name);
5931 plist.add (ARDOUR::Properties::layer, 0);
5933 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5934 /* because we set annouce to false, manually add the new region to the
5937 RegionFactory::map_add (nr);
5939 pl->add_region (nr, r->position() + pos);
5942 new_regions.push_front(nr);
5951 RegionFactory::region_name (new_name, r->name());
5953 /* Add the final region */
5956 plist.add (ARDOUR::Properties::start, r->start() + pos);
5957 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5958 plist.add (ARDOUR::Properties::name, new_name);
5959 plist.add (ARDOUR::Properties::layer, 0);
5961 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5962 /* because we set annouce to false, manually add the new region to the
5965 RegionFactory::map_add (nr);
5966 pl->add_region (nr, r->position() + pos);
5969 new_regions.push_front(nr);
5974 /* We might have removed regions, which alters other regions' layering_index,
5975 so we need to do a recursive diff here.
5977 vector<Command*> cmds;
5979 _session->add_commands (cmds);
5981 _session->add_command (new StatefulDiffCommand (pl));
5985 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5986 set_selected_regionview_from_region_list ((*i), Selection::Add);
5992 Editor::place_transient()
5998 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6004 framepos_t where = get_preferred_edit_position();
6006 _session->begin_reversible_command (_("place transient"));
6008 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6009 framepos_t position = (*r)->region()->position();
6010 (*r)->region()->add_transient(where - position);
6013 _session->commit_reversible_command ();
6017 Editor::remove_transient(ArdourCanvas::Item* item)
6023 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6026 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6027 _arv->remove_transient (*(float*) _line->get_data ("position"));
6031 Editor::snap_regions_to_grid ()
6033 list <boost::shared_ptr<Playlist > > used_playlists;
6035 RegionSelection rs = get_regions_from_selection_and_entered ();
6037 if (!_session || rs.empty()) {
6041 _session->begin_reversible_command (_("snap regions to grid"));
6043 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6045 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6047 if (!pl->frozen()) {
6048 /* we haven't seen this playlist before */
6050 /* remember used playlists so we can thaw them later */
6051 used_playlists.push_back(pl);
6055 framepos_t start_frame = (*r)->region()->first_frame ();
6056 snap_to (start_frame);
6057 (*r)->region()->set_position (start_frame);
6060 while (used_playlists.size() > 0) {
6061 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6063 used_playlists.pop_front();
6066 _session->commit_reversible_command ();
6070 Editor::close_region_gaps ()
6072 list <boost::shared_ptr<Playlist > > used_playlists;
6074 RegionSelection rs = get_regions_from_selection_and_entered ();
6076 if (!_session || rs.empty()) {
6080 Dialog dialog (_("Close Region Gaps"));
6083 table.set_spacings (12);
6084 table.set_border_width (12);
6085 Label* l = manage (left_aligned_label (_("Crossfade length")));
6086 table.attach (*l, 0, 1, 0, 1);
6088 SpinButton spin_crossfade (1, 0);
6089 spin_crossfade.set_range (0, 15);
6090 spin_crossfade.set_increments (1, 1);
6091 spin_crossfade.set_value (5);
6092 table.attach (spin_crossfade, 1, 2, 0, 1);
6094 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6096 l = manage (left_aligned_label (_("Pull-back length")));
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 ();