2 Copyright (C) 2000-2004 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
30 #include "pbd/error.h"
31 #include "pbd/basename.h"
32 #include "pbd/pthread_utils.h"
33 #include "pbd/memento_command.h"
34 #include "pbd/unwind.h"
35 #include "pbd/whitespace.h"
36 #include "pbd/stateful_diff_command.h"
38 #include <gtkmm2ext/utils.h>
39 #include <gtkmm2ext/choice.h>
40 #include <gtkmm2ext/popup.h>
42 #include "ardour/audio_track.h"
43 #include "ardour/audioregion.h"
44 #include "ardour/dB.h"
45 #include "ardour/location.h"
46 #include "ardour/midi_region.h"
47 #include "ardour/operations.h"
48 #include "ardour/playlist_factory.h"
49 #include "ardour/quantize.h"
50 #include "ardour/region_factory.h"
51 #include "ardour/reverse.h"
52 #include "ardour/session.h"
53 #include "ardour/session_playlists.h"
54 #include "ardour/strip_silence.h"
55 #include "ardour/transient_detector.h"
57 #include "ardour_ui.h"
60 #include "time_axis_view.h"
61 #include "route_time_axis.h"
62 #include "audio_time_axis.h"
63 #include "automation_time_axis.h"
64 #include "control_point.h"
65 #include "streamview.h"
66 #include "audio_streamview.h"
67 #include "audio_region_view.h"
68 #include "midi_region_view.h"
69 #include "rgb_macros.h"
70 #include "selection_templates.h"
71 #include "selection.h"
73 #include "gtk-custom-hruler.h"
74 #include "gui_thread.h"
77 #include "editor_drag.h"
78 #include "strip_silence_dialog.h"
79 #include "editor_routes.h"
80 #include "editor_regions.h"
81 #include "quantize_dialog.h"
82 #include "interthread_progress_window.h"
83 #include "insert_time_dialog.h"
84 #include "normalize_dialog.h"
85 #include "editor_cursors.h"
86 #include "mouse_cursors.h"
87 #include "patch_change_dialog.h"
88 #include "transpose_dialog.h"
93 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
98 using Gtkmm2ext::Keyboard;
100 /***********************************************************************
102 ***********************************************************************/
105 Editor::undo (uint32_t n)
107 if (_drags->active ()) {
117 Editor::redo (uint32_t n)
119 if (_drags->active ()) {
129 Editor::split_regions_at (framepos_t where, RegionSelection& regions)
133 list <boost::shared_ptr<Playlist > > used_playlists;
135 if (regions.empty()) {
139 begin_reversible_command (_("split"));
141 // if splitting a single region, and snap-to is using
142 // region boundaries, don't pay attention to them
144 if (regions.size() == 1) {
145 switch (_snap_type) {
146 case SnapToRegionStart:
147 case SnapToRegionSync:
148 case SnapToRegionEnd:
157 EditorFreeze(); /* Emit Signal */
160 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
162 RegionSelection::iterator tmp;
164 /* XXX this test needs to be more complicated, to make sure we really
165 have something to split.
168 if (!(*a)->region()->covers (where)) {
176 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
184 /* we haven't seen this playlist before */
186 /* remember used playlists so we can thaw them later */
187 used_playlists.push_back(pl);
192 pl->clear_changes ();
193 pl->split_region ((*a)->region(), where);
194 _session->add_command (new StatefulDiffCommand (pl));
200 while (used_playlists.size() > 0) {
201 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
203 used_playlists.pop_front();
206 commit_reversible_command ();
209 EditorThaw(); /* Emit Signal */
213 /** Move one extreme of the current range selection. If more than one range is selected,
214 * the start of the earliest range or the end of the latest range is moved.
216 * @param move_end true to move the end of the current range selection, false to move
218 * @param next true to move the extreme to the next region boundary, false to move to
222 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
224 if (selection->time.start() == selection->time.end_frame()) {
228 framepos_t start = selection->time.start ();
229 framepos_t end = selection->time.end_frame ();
231 /* the position of the thing we may move */
232 framepos_t pos = move_end ? end : start;
233 int dir = next ? 1 : -1;
235 /* so we don't find the current region again */
236 if (dir > 0 || pos > 0) {
240 framepos_t const target = get_region_boundary (pos, dir, true, false);
255 begin_reversible_command (_("alter selection"));
256 selection->set_preserving_all_ranges (start, end);
257 commit_reversible_command ();
261 Editor::nudge_forward_release (GdkEventButton* ev)
263 if (ev->state & Keyboard::PrimaryModifier) {
264 nudge_forward (false, true);
266 nudge_forward (false, false);
272 Editor::nudge_backward_release (GdkEventButton* ev)
274 if (ev->state & Keyboard::PrimaryModifier) {
275 nudge_backward (false, true);
277 nudge_backward (false, false);
284 Editor::nudge_forward (bool next, bool force_playhead)
287 framepos_t next_distance;
293 RegionSelection rs = get_regions_from_selection_and_entered ();
295 if (!force_playhead && !rs.empty()) {
297 begin_reversible_command (_("nudge regions forward"));
299 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
300 boost::shared_ptr<Region> r ((*i)->region());
302 distance = get_nudge_distance (r->position(), next_distance);
305 distance = next_distance;
309 r->set_position (r->position() + distance);
310 _session->add_command (new StatefulDiffCommand (r));
313 commit_reversible_command ();
316 } else if (!force_playhead && !selection->markers.empty()) {
320 begin_reversible_command (_("nudge location forward"));
322 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
324 Location* loc = find_location_from_marker ((*i), is_start);
328 XMLNode& before (loc->get_state());
331 distance = get_nudge_distance (loc->start(), next_distance);
333 distance = next_distance;
335 if (max_framepos - distance > loc->start() + loc->length()) {
336 loc->set_start (loc->start() + distance);
338 loc->set_start (max_framepos - loc->length());
341 distance = get_nudge_distance (loc->end(), next_distance);
343 distance = next_distance;
345 if (max_framepos - distance > loc->end()) {
346 loc->set_end (loc->end() + distance);
348 loc->set_end (max_framepos);
351 XMLNode& after (loc->get_state());
352 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
356 commit_reversible_command ();
359 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
360 _session->request_locate (playhead_cursor->current_frame + distance);
365 Editor::nudge_backward (bool next, bool force_playhead)
368 framepos_t next_distance;
374 RegionSelection rs = get_regions_from_selection_and_entered ();
376 if (!force_playhead && !rs.empty()) {
378 begin_reversible_command (_("nudge regions backward"));
380 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
381 boost::shared_ptr<Region> r ((*i)->region());
383 distance = get_nudge_distance (r->position(), next_distance);
386 distance = next_distance;
391 if (r->position() > distance) {
392 r->set_position (r->position() - distance);
396 _session->add_command (new StatefulDiffCommand (r));
399 commit_reversible_command ();
401 } else if (!force_playhead && !selection->markers.empty()) {
405 begin_reversible_command (_("nudge location forward"));
407 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
409 Location* loc = find_location_from_marker ((*i), is_start);
413 XMLNode& before (loc->get_state());
416 distance = get_nudge_distance (loc->start(), next_distance);
418 distance = next_distance;
420 if (distance < loc->start()) {
421 loc->set_start (loc->start() - distance);
426 distance = get_nudge_distance (loc->end(), next_distance);
429 distance = next_distance;
432 if (distance < loc->end() - loc->length()) {
433 loc->set_end (loc->end() - distance);
435 loc->set_end (loc->length());
439 XMLNode& after (loc->get_state());
440 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
444 commit_reversible_command ();
448 distance = get_nudge_distance (playhead_cursor->current_frame, next_distance);
450 if (playhead_cursor->current_frame > distance) {
451 _session->request_locate (playhead_cursor->current_frame - distance);
453 _session->goto_start();
459 Editor::nudge_forward_capture_offset ()
461 RegionSelection rs = get_regions_from_selection_and_entered ();
463 if (!_session || rs.empty()) {
467 begin_reversible_command (_("nudge forward"));
469 framepos_t const distance = _session->worst_output_latency();
471 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
472 boost::shared_ptr<Region> r ((*i)->region());
475 r->set_position (r->position() + distance);
476 _session->add_command(new StatefulDiffCommand (r));
479 commit_reversible_command ();
483 Editor::nudge_backward_capture_offset ()
485 RegionSelection rs = get_regions_from_selection_and_entered ();
487 if (!_session || rs.empty()) {
491 begin_reversible_command (_("nudge backward"));
493 framepos_t const distance = _session->worst_output_latency();
495 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
496 boost::shared_ptr<Region> r ((*i)->region());
500 if (r->position() > distance) {
501 r->set_position (r->position() - distance);
505 _session->add_command(new StatefulDiffCommand (r));
508 commit_reversible_command ();
514 Editor::move_to_start ()
516 _session->goto_start ();
520 Editor::move_to_end ()
523 _session->request_locate (_session->current_end_frame());
527 Editor::build_region_boundary_cache ()
530 vector<RegionPoint> interesting_points;
531 boost::shared_ptr<Region> r;
532 TrackViewList tracks;
535 region_boundary_cache.clear ();
541 switch (_snap_type) {
542 case SnapToRegionStart:
543 interesting_points.push_back (Start);
545 case SnapToRegionEnd:
546 interesting_points.push_back (End);
548 case SnapToRegionSync:
549 interesting_points.push_back (SyncPoint);
551 case SnapToRegionBoundary:
552 interesting_points.push_back (Start);
553 interesting_points.push_back (End);
556 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
561 TimeAxisView *ontrack = 0;
564 if (!selection->tracks.empty()) {
565 tlist = selection->tracks.filter_to_unique_playlists ();
567 tlist = track_views.filter_to_unique_playlists ();
570 while (pos < _session->current_end_frame() && !at_end) {
573 framepos_t lpos = max_framepos;
575 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
577 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
578 if (*p == interesting_points.back()) {
581 /* move to next point type */
587 rpos = r->first_frame();
591 rpos = r->last_frame();
595 rpos = r->sync_position ();
603 RouteTimeAxisView *rtav;
605 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
606 if (rtav->track() != 0) {
607 speed = rtav->track()->speed();
611 rpos = track_frame_to_session_frame (rpos, speed);
617 /* prevent duplicates, but we don't use set<> because we want to be able
621 vector<framepos_t>::iterator ri;
623 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
629 if (ri == region_boundary_cache.end()) {
630 region_boundary_cache.push_back (rpos);
637 /* finally sort to be sure that the order is correct */
639 sort (region_boundary_cache.begin(), region_boundary_cache.end());
642 boost::shared_ptr<Region>
643 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
645 TrackViewList::iterator i;
646 framepos_t closest = max_framepos;
647 boost::shared_ptr<Region> ret;
651 framepos_t track_frame;
652 RouteTimeAxisView *rtav;
654 for (i = tracks.begin(); i != tracks.end(); ++i) {
657 boost::shared_ptr<Region> r;
660 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
661 if (rtav->track()!=0)
662 track_speed = rtav->track()->speed();
665 track_frame = session_frame_to_track_frame(frame, track_speed);
667 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
673 rpos = r->first_frame ();
677 rpos = r->last_frame ();
681 rpos = r->sync_position ();
685 // rpos is a "track frame", converting it to "_session frame"
686 rpos = track_frame_to_session_frame(rpos, track_speed);
689 distance = rpos - frame;
691 distance = frame - rpos;
694 if (distance < closest) {
706 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
708 framecnt_t distance = max_framepos;
709 framepos_t current_nearest = -1;
711 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
712 framepos_t contender;
715 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
721 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
725 d = ::llabs (pos - contender);
728 current_nearest = contender;
733 return current_nearest;
737 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
742 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
744 if (!selection->tracks.empty()) {
746 target = find_next_region_boundary (pos, dir, selection->tracks);
750 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
751 get_onscreen_tracks (tvl);
752 target = find_next_region_boundary (pos, dir, tvl);
754 target = find_next_region_boundary (pos, dir, track_views);
760 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
761 get_onscreen_tracks (tvl);
762 target = find_next_region_boundary (pos, dir, tvl);
764 target = find_next_region_boundary (pos, dir, track_views);
772 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
774 framepos_t pos = playhead_cursor->current_frame;
781 // so we don't find the current region again..
782 if (dir > 0 || pos > 0) {
786 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
790 _session->request_locate (target);
794 Editor::cursor_to_next_region_boundary (bool with_selection)
796 cursor_to_region_boundary (with_selection, 1);
800 Editor::cursor_to_previous_region_boundary (bool with_selection)
802 cursor_to_region_boundary (with_selection, -1);
806 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
808 boost::shared_ptr<Region> r;
809 framepos_t pos = cursor->current_frame;
815 TimeAxisView *ontrack = 0;
817 // so we don't find the current region again..
821 if (!selection->tracks.empty()) {
823 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
825 } else if (clicked_axisview) {
828 t.push_back (clicked_axisview);
830 r = find_next_region (pos, point, dir, t, &ontrack);
834 r = find_next_region (pos, point, dir, track_views, &ontrack);
843 pos = r->first_frame ();
847 pos = r->last_frame ();
851 pos = r->sync_position ();
856 RouteTimeAxisView *rtav;
858 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
859 if (rtav->track() != 0) {
860 speed = rtav->track()->speed();
864 pos = track_frame_to_session_frame(pos, speed);
866 if (cursor == playhead_cursor) {
867 _session->request_locate (pos);
869 cursor->set_position (pos);
874 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
876 cursor_to_region_point (cursor, point, 1);
880 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
882 cursor_to_region_point (cursor, point, -1);
886 Editor::cursor_to_selection_start (EditorCursor *cursor)
890 switch (mouse_mode) {
892 if (!selection->regions.empty()) {
893 pos = selection->regions.start();
898 if (!selection->time.empty()) {
899 pos = selection->time.start ();
907 if (cursor == playhead_cursor) {
908 _session->request_locate (pos);
910 cursor->set_position (pos);
915 Editor::cursor_to_selection_end (EditorCursor *cursor)
919 switch (mouse_mode) {
921 if (!selection->regions.empty()) {
922 pos = selection->regions.end_frame();
927 if (!selection->time.empty()) {
928 pos = selection->time.end_frame ();
936 if (cursor == playhead_cursor) {
937 _session->request_locate (pos);
939 cursor->set_position (pos);
944 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
954 if (selection->markers.empty()) {
958 if (!mouse_frame (mouse, ignored)) {
962 add_location_mark (mouse);
965 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
969 framepos_t pos = loc->start();
971 // so we don't find the current region again..
972 if (dir > 0 || pos > 0) {
976 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
980 loc->move_to (target);
984 Editor::selected_marker_to_next_region_boundary (bool with_selection)
986 selected_marker_to_region_boundary (with_selection, 1);
990 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
992 selected_marker_to_region_boundary (with_selection, -1);
996 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
998 boost::shared_ptr<Region> r;
1003 if (!_session || selection->markers.empty()) {
1007 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1011 TimeAxisView *ontrack = 0;
1015 // so we don't find the current region again..
1019 if (!selection->tracks.empty()) {
1021 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1025 r = find_next_region (pos, point, dir, track_views, &ontrack);
1034 pos = r->first_frame ();
1038 pos = r->last_frame ();
1042 pos = r->adjust_to_sync (r->first_frame());
1047 RouteTimeAxisView *rtav;
1049 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1050 if (rtav->track() != 0) {
1051 speed = rtav->track()->speed();
1055 pos = track_frame_to_session_frame(pos, speed);
1061 Editor::selected_marker_to_next_region_point (RegionPoint point)
1063 selected_marker_to_region_point (point, 1);
1067 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1069 selected_marker_to_region_point (point, -1);
1073 Editor::selected_marker_to_selection_start ()
1079 if (!_session || selection->markers.empty()) {
1083 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1087 switch (mouse_mode) {
1089 if (!selection->regions.empty()) {
1090 pos = selection->regions.start();
1095 if (!selection->time.empty()) {
1096 pos = selection->time.start ();
1108 Editor::selected_marker_to_selection_end ()
1114 if (!_session || selection->markers.empty()) {
1118 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1122 switch (mouse_mode) {
1124 if (!selection->regions.empty()) {
1125 pos = selection->regions.end_frame();
1130 if (!selection->time.empty()) {
1131 pos = selection->time.end_frame ();
1143 Editor::scroll_playhead (bool forward)
1145 framepos_t pos = playhead_cursor->current_frame;
1146 framecnt_t delta = (framecnt_t) floor (current_page_frames() / 0.8);
1149 if (pos == max_framepos) {
1153 if (pos < max_framepos - delta) {
1172 _session->request_locate (pos);
1176 Editor::cursor_align (bool playhead_to_edit)
1182 if (playhead_to_edit) {
1184 if (selection->markers.empty()) {
1188 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1191 /* move selected markers to playhead */
1193 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1196 Location* loc = find_location_from_marker (*i, ignored);
1198 if (loc->is_mark()) {
1199 loc->set_start (playhead_cursor->current_frame);
1201 loc->set (playhead_cursor->current_frame,
1202 playhead_cursor->current_frame + loc->length());
1209 Editor::scroll_backward (float pages)
1211 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1212 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1215 if (leftmost_frame < cnt) {
1218 frame = leftmost_frame - cnt;
1221 reset_x_origin (frame);
1225 Editor::scroll_forward (float pages)
1227 framepos_t const one_page = (framepos_t) rint (_canvas_width * frames_per_unit);
1228 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1231 if (max_framepos - cnt < leftmost_frame) {
1232 frame = max_framepos - cnt;
1234 frame = leftmost_frame + cnt;
1237 reset_x_origin (frame);
1241 Editor::scroll_tracks_down ()
1243 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1244 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1245 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1248 vertical_adjustment.set_value (vert_value);
1252 Editor::scroll_tracks_up ()
1254 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1258 Editor::scroll_tracks_down_line ()
1260 double vert_value = vertical_adjustment.get_value() + 60;
1262 if (vert_value > vertical_adjustment.get_upper() - _canvas_height) {
1263 vert_value = vertical_adjustment.get_upper() - _canvas_height;
1266 vertical_adjustment.set_value (vert_value);
1270 Editor::scroll_tracks_up_line ()
1272 reset_y_origin (vertical_adjustment.get_value() - 60);
1278 Editor::tav_zoom_step (bool coarser)
1280 _routes->suspend_redisplay ();
1284 if (selection->tracks.empty()) {
1287 ts = &selection->tracks;
1290 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1291 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1292 tv->step_height (coarser);
1295 _routes->resume_redisplay ();
1299 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1301 _routes->suspend_redisplay ();
1305 if (selection->tracks.empty() || force_all) {
1308 ts = &selection->tracks;
1311 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1312 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1313 uint32_t h = tv->current_height ();
1318 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1323 tv->set_height (h + 5);
1327 _routes->resume_redisplay ();
1331 Editor::temporal_zoom_step (bool coarser)
1333 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
1337 nfpu = frames_per_unit;
1342 nfpu = max(1.0,(nfpu/1.61803399));
1345 temporal_zoom (nfpu);
1349 Editor::temporal_zoom (gdouble fpu)
1351 if (!_session) return;
1353 framepos_t current_page = current_page_frames();
1354 framepos_t current_leftmost = leftmost_frame;
1355 framepos_t current_rightmost;
1356 framepos_t current_center;
1357 framepos_t new_page_size;
1358 framepos_t half_page_size;
1359 framepos_t leftmost_after_zoom = 0;
1361 bool in_track_canvas;
1365 /* XXX this limit is also in ::set_frames_per_unit() */
1367 if (frames_per_unit <= 1.0 && fpu <= frames_per_unit) {
1373 new_page_size = (framepos_t) floor (_canvas_width * nfpu);
1374 half_page_size = new_page_size / 2;
1376 switch (zoom_focus) {
1378 leftmost_after_zoom = current_leftmost;
1381 case ZoomFocusRight:
1382 current_rightmost = leftmost_frame + current_page;
1383 if (current_rightmost < new_page_size) {
1384 leftmost_after_zoom = 0;
1386 leftmost_after_zoom = current_rightmost - new_page_size;
1390 case ZoomFocusCenter:
1391 current_center = current_leftmost + (current_page/2);
1392 if (current_center < half_page_size) {
1393 leftmost_after_zoom = 0;
1395 leftmost_after_zoom = current_center - half_page_size;
1399 case ZoomFocusPlayhead:
1400 /* centre playhead */
1401 l = playhead_cursor->current_frame - (new_page_size * 0.5);
1404 leftmost_after_zoom = 0;
1405 } else if (l > max_framepos) {
1406 leftmost_after_zoom = max_framepos - new_page_size;
1408 leftmost_after_zoom = (framepos_t) l;
1412 case ZoomFocusMouse:
1413 /* try to keep the mouse over the same point in the display */
1415 if (!mouse_frame (where, in_track_canvas)) {
1416 /* use playhead instead */
1417 where = playhead_cursor->current_frame;
1419 if (where < half_page_size) {
1420 leftmost_after_zoom = 0;
1422 leftmost_after_zoom = where - half_page_size;
1427 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1430 leftmost_after_zoom = 0;
1431 } else if (l > max_framepos) {
1432 leftmost_after_zoom = max_framepos - new_page_size;
1434 leftmost_after_zoom = (framepos_t) l;
1441 /* try to keep the edit point in the same place */
1442 where = get_preferred_edit_position ();
1446 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1449 leftmost_after_zoom = 0;
1450 } else if (l > max_framepos) {
1451 leftmost_after_zoom = max_framepos - new_page_size;
1453 leftmost_after_zoom = (framepos_t) l;
1457 /* edit point not defined */
1464 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1466 reposition_and_zoom (leftmost_after_zoom, nfpu);
1470 Editor::temporal_zoom_region (bool both_axes)
1472 framepos_t start = max_framepos;
1474 set<TimeAxisView*> tracks;
1476 RegionSelection rs = get_regions_from_selection_and_entered ();
1482 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1484 if ((*i)->region()->position() < start) {
1485 start = (*i)->region()->position();
1488 if ((*i)->region()->last_frame() + 1 > end) {
1489 end = (*i)->region()->last_frame() + 1;
1492 tracks.insert (&((*i)->get_time_axis_view()));
1495 /* now comes an "interesting" hack ... make sure we leave a little space
1496 at each end of the editor so that the zoom doesn't fit the region
1497 precisely to the screen.
1500 GdkScreen* screen = gdk_screen_get_default ();
1501 gint pixwidth = gdk_screen_get_width (screen);
1502 gint mmwidth = gdk_screen_get_width_mm (screen);
1503 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1504 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1506 if ((start == 0 && end == 0) || end < start) {
1510 framepos_t range = end - start;
1511 double new_fpu = (double)range / (double)_canvas_width;
1512 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
1514 if (start > extra_samples) {
1515 start -= extra_samples;
1520 if (max_framepos - extra_samples > end) {
1521 end += extra_samples;
1526 /* if we're zooming on both axes we need to save track heights etc.
1529 undo_visual_stack.push_back (current_visual_state (both_axes));
1531 PBD::Unwinder<bool> nsv (no_save_visual, true);
1533 temporal_zoom_by_frame (start, end);
1536 uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1538 /* set visible track heights appropriately */
1540 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1541 (*t)->set_height (per_track_height);
1544 /* hide irrelevant tracks */
1546 _routes->suspend_redisplay ();
1548 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1549 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1550 hide_track_in_display (*i);
1554 _routes->resume_redisplay ();
1556 vertical_adjustment.set_value (0.0);
1559 redo_visual_stack.push_back (current_visual_state (both_axes));
1563 Editor::zoom_to_region (bool both_axes)
1565 temporal_zoom_region (both_axes);
1569 Editor::temporal_zoom_selection ()
1571 if (!selection) return;
1573 if (selection->time.empty()) {
1577 framepos_t start = selection->time[clicked_selection].start;
1578 framepos_t end = selection->time[clicked_selection].end;
1580 temporal_zoom_by_frame (start, end);
1584 Editor::temporal_zoom_session ()
1586 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1589 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1590 double s = _session->current_start_frame() - l * 0.01;
1594 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1595 temporal_zoom_by_frame (framecnt_t (s), e);
1600 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1602 if (!_session) return;
1604 if ((start == 0 && end == 0) || end < start) {
1608 framepos_t range = end - start;
1610 double new_fpu = (double)range / (double)_canvas_width;
1612 framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
1613 framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
1614 framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
1616 if (new_leftmost > middle) {
1620 if (new_leftmost < 0) {
1624 reposition_and_zoom (new_leftmost, new_fpu);
1628 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1633 double range_before = frame - leftmost_frame;
1636 new_fpu = frames_per_unit;
1639 new_fpu *= 1.61803399;
1640 range_before *= 1.61803399;
1642 new_fpu = max(1.0,(new_fpu/1.61803399));
1643 range_before /= 1.61803399;
1646 if (new_fpu == frames_per_unit) {
1650 framepos_t new_leftmost = frame - (framepos_t)range_before;
1652 if (new_leftmost > frame) {
1656 if (new_leftmost < 0) {
1660 reposition_and_zoom (new_leftmost, new_fpu);
1665 Editor::choose_new_marker_name(string &name) {
1667 if (!Config->get_name_new_markers()) {
1668 /* don't prompt user for a new name */
1672 ArdourPrompter dialog (true);
1674 dialog.set_prompt (_("New Name:"));
1676 dialog.set_title (_("New Location Marker"));
1678 dialog.set_name ("MarkNameWindow");
1679 dialog.set_size_request (250, -1);
1680 dialog.set_position (Gtk::WIN_POS_MOUSE);
1682 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1683 dialog.set_initial_text (name);
1687 switch (dialog.run ()) {
1688 case RESPONSE_ACCEPT:
1694 dialog.get_result(name);
1701 Editor::add_location_from_selection ()
1705 if (selection->time.empty()) {
1709 if (_session == 0 || clicked_axisview == 0) {
1713 framepos_t start = selection->time[clicked_selection].start;
1714 framepos_t end = selection->time[clicked_selection].end;
1716 _session->locations()->next_available_name(rangename,"selection");
1717 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1719 _session->begin_reversible_command (_("add marker"));
1720 XMLNode &before = _session->locations()->get_state();
1721 _session->locations()->add (location, true);
1722 XMLNode &after = _session->locations()->get_state();
1723 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1724 _session->commit_reversible_command ();
1728 Editor::add_location_mark (framepos_t where)
1732 select_new_marker = true;
1734 _session->locations()->next_available_name(markername,"mark");
1735 if (!choose_new_marker_name(markername)) {
1738 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1739 _session->begin_reversible_command (_("add marker"));
1740 XMLNode &before = _session->locations()->get_state();
1741 _session->locations()->add (location, true);
1742 XMLNode &after = _session->locations()->get_state();
1743 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1744 _session->commit_reversible_command ();
1748 Editor::add_location_from_playhead_cursor ()
1750 add_location_mark (_session->audible_frame());
1753 /** Add a range marker around each selected region */
1755 Editor::add_locations_from_region ()
1757 RegionSelection rs = get_regions_from_selection_and_entered ();
1763 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1764 XMLNode &before = _session->locations()->get_state();
1766 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1768 boost::shared_ptr<Region> region = (*i)->region ();
1770 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1772 _session->locations()->add (location, true);
1775 XMLNode &after = _session->locations()->get_state();
1776 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1777 _session->commit_reversible_command ();
1780 /** Add a single range marker around all selected regions */
1782 Editor::add_location_from_region ()
1784 RegionSelection rs = get_regions_from_selection_and_entered ();
1790 _session->begin_reversible_command (_("add marker"));
1791 XMLNode &before = _session->locations()->get_state();
1795 if (rs.size() > 1) {
1796 _session->locations()->next_available_name(markername, "regions");
1798 RegionView* rv = *(rs.begin());
1799 boost::shared_ptr<Region> region = rv->region();
1800 markername = region->name();
1803 if (!choose_new_marker_name(markername)) {
1807 // single range spanning all selected
1808 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1809 _session->locations()->add (location, true);
1811 XMLNode &after = _session->locations()->get_state();
1812 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1813 _session->commit_reversible_command ();
1819 Editor::jump_forward_to_mark ()
1825 Location *location = _session->locations()->first_location_after (playhead_cursor->current_frame);
1828 _session->request_locate (location->start(), _session->transport_rolling());
1830 _session->request_locate (_session->current_end_frame());
1835 Editor::jump_backward_to_mark ()
1841 Location *location = _session->locations()->first_location_before (playhead_cursor->current_frame);
1844 _session->request_locate (location->start(), _session->transport_rolling());
1846 _session->goto_start ();
1853 framepos_t const pos = _session->audible_frame ();
1856 _session->locations()->next_available_name (markername, "mark");
1858 if (!choose_new_marker_name (markername)) {
1862 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1866 Editor::clear_markers ()
1869 _session->begin_reversible_command (_("clear markers"));
1870 XMLNode &before = _session->locations()->get_state();
1871 _session->locations()->clear_markers ();
1872 XMLNode &after = _session->locations()->get_state();
1873 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1874 _session->commit_reversible_command ();
1879 Editor::clear_ranges ()
1882 _session->begin_reversible_command (_("clear ranges"));
1883 XMLNode &before = _session->locations()->get_state();
1885 Location * looploc = _session->locations()->auto_loop_location();
1886 Location * punchloc = _session->locations()->auto_punch_location();
1887 Location * sessionloc = _session->locations()->session_range_location();
1889 _session->locations()->clear_ranges ();
1891 if (looploc) _session->locations()->add (looploc);
1892 if (punchloc) _session->locations()->add (punchloc);
1893 if (sessionloc) _session->locations()->add (sessionloc);
1895 XMLNode &after = _session->locations()->get_state();
1896 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1897 _session->commit_reversible_command ();
1902 Editor::clear_locations ()
1904 _session->begin_reversible_command (_("clear locations"));
1905 XMLNode &before = _session->locations()->get_state();
1906 _session->locations()->clear ();
1907 XMLNode &after = _session->locations()->get_state();
1908 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1909 _session->commit_reversible_command ();
1910 _session->locations()->clear ();
1914 Editor::unhide_markers ()
1916 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1917 Location *l = (*i).first;
1918 if (l->is_hidden() && l->is_mark()) {
1919 l->set_hidden(false, this);
1925 Editor::unhide_ranges ()
1927 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1928 Location *l = (*i).first;
1929 if (l->is_hidden() && l->is_range_marker()) {
1930 l->set_hidden(false, this);
1935 /* INSERT/REPLACE */
1938 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1943 RouteTimeAxisView *rtv = 0;
1944 boost::shared_ptr<Playlist> playlist;
1946 track_canvas->window_to_world (x, y, wx, wy);
1949 event.type = GDK_BUTTON_RELEASE;
1950 event.button.x = wx;
1951 event.button.y = wy;
1953 where = event_frame (&event, &cx, &cy);
1955 if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1956 /* clearly outside canvas area */
1960 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1961 if (tv.first == 0) {
1965 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1969 if ((playlist = rtv->playlist()) == 0) {
1975 begin_reversible_command (_("insert dragged region"));
1976 playlist->clear_changes ();
1977 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
1978 _session->add_command(new StatefulDiffCommand (playlist));
1979 commit_reversible_command ();
1983 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
1987 RouteTimeAxisView *dest_rtv = 0;
1988 RouteTimeAxisView *source_rtv = 0;
1990 track_canvas->window_to_world (x, y, wx, wy);
1991 wx += horizontal_position ();
1992 wy += vertical_adjustment.get_value();
1995 event.type = GDK_BUTTON_RELEASE;
1996 event.button.x = wx;
1997 event.button.y = wy;
1999 event_frame (&event, &cx, &cy);
2001 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2002 if (tv.first == 0) {
2006 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2010 /* use this drag source to add underlay to a track. But we really don't care
2011 about the Route, only the view of the route, so find it first */
2012 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2013 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2017 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2018 dest_rtv->add_underlay(source_rtv->view());
2025 Editor::insert_region_list_selection (float times)
2027 RouteTimeAxisView *tv = 0;
2028 boost::shared_ptr<Playlist> playlist;
2030 if (clicked_routeview != 0) {
2031 tv = clicked_routeview;
2032 } else if (!selection->tracks.empty()) {
2033 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2036 } else if (entered_track != 0) {
2037 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2044 if ((playlist = tv->playlist()) == 0) {
2048 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2053 begin_reversible_command (_("insert region"));
2054 playlist->clear_changes ();
2055 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2056 _session->add_command(new StatefulDiffCommand (playlist));
2057 commit_reversible_command ();
2060 /* BUILT-IN EFFECTS */
2063 Editor::reverse_selection ()
2068 /* GAIN ENVELOPE EDITING */
2071 Editor::edit_envelope ()
2078 Editor::transition_to_rolling (bool fwd)
2084 if (_session->config.get_external_sync()) {
2085 switch (_session->config.get_sync_source()) {
2089 /* transport controlled by the master */
2094 if (_session->is_auditioning()) {
2095 _session->cancel_audition ();
2099 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2103 Editor::play_from_start ()
2105 _session->request_locate (_session->current_start_frame(), true);
2109 Editor::play_from_edit_point ()
2111 _session->request_locate (get_preferred_edit_position(), true);
2115 Editor::play_from_edit_point_and_return ()
2117 framepos_t start_frame;
2118 framepos_t return_frame;
2120 start_frame = get_preferred_edit_position (true);
2122 if (_session->transport_rolling()) {
2123 _session->request_locate (start_frame, false);
2127 /* don't reset the return frame if its already set */
2129 if ((return_frame = _session->requested_return_frame()) < 0) {
2130 return_frame = _session->audible_frame();
2133 if (start_frame >= 0) {
2134 _session->request_roll_at_and_return (start_frame, return_frame);
2139 Editor::play_selection ()
2141 if (selection->time.empty()) {
2145 _session->request_play_range (&selection->time, true);
2149 Editor::play_location (Location& location)
2151 if (location.start() <= location.end()) {
2155 _session->request_bounded_roll (location.start(), location.end());
2159 Editor::loop_location (Location& location)
2161 if (location.start() <= location.end()) {
2167 if ((tll = transport_loop_location()) != 0) {
2168 tll->set (location.start(), location.end());
2170 // enable looping, reposition and start rolling
2171 _session->request_play_loop (true);
2172 _session->request_locate (tll->start(), true);
2177 Editor::do_layer_operation (LayerOperation op)
2179 if (selection->regions.empty ()) {
2183 bool const multiple = selection->regions.size() > 1;
2187 begin_reversible_command (_("raise regions"));
2189 begin_reversible_command (_("raise region"));
2195 begin_reversible_command (_("raise regions to top"));
2197 begin_reversible_command (_("raise region to top"));
2203 begin_reversible_command (_("lower regions"));
2205 begin_reversible_command (_("lower region"));
2211 begin_reversible_command (_("lower regions to bottom"));
2213 begin_reversible_command (_("lower region"));
2218 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2219 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2220 (*i)->clear_owned_changes ();
2223 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2224 boost::shared_ptr<Region> r = (*i)->region ();
2236 r->lower_to_bottom ();
2240 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2241 vector<Command*> cmds;
2243 _session->add_commands (cmds);
2246 commit_reversible_command ();
2250 Editor::raise_region ()
2252 do_layer_operation (Raise);
2256 Editor::raise_region_to_top ()
2258 do_layer_operation (RaiseToTop);
2262 Editor::lower_region ()
2264 do_layer_operation (Lower);
2268 Editor::lower_region_to_bottom ()
2270 do_layer_operation (LowerToBottom);
2273 /** Show the region editor for the selected regions */
2275 Editor::show_region_properties ()
2277 selection->foreach_regionview (&RegionView::show_region_editor);
2280 /** Show the midi list editor for the selected MIDI regions */
2282 Editor::show_midi_list_editor ()
2284 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2288 Editor::rename_region ()
2290 RegionSelection rs = get_regions_from_selection_and_entered ();
2296 ArdourDialog d (*this, _("Rename Region"), true, false);
2298 Label label (_("New name:"));
2301 hbox.set_spacing (6);
2302 hbox.pack_start (label, false, false);
2303 hbox.pack_start (entry, true, true);
2305 d.get_vbox()->set_border_width (12);
2306 d.get_vbox()->pack_start (hbox, false, false);
2308 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2309 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2311 d.set_size_request (300, -1);
2312 d.set_position (Gtk::WIN_POS_MOUSE);
2314 entry.set_text (rs.front()->region()->name());
2315 entry.select_region (0, -1);
2317 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2323 int const ret = d.run();
2327 if (ret != RESPONSE_OK) {
2331 std::string str = entry.get_text();
2332 strip_whitespace_edges (str);
2334 rs.front()->region()->set_name (str);
2335 _regions->redisplay ();
2340 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2342 if (_session->is_auditioning()) {
2343 _session->cancel_audition ();
2346 // note: some potential for creativity here, because region doesn't
2347 // have to belong to the playlist that Route is handling
2349 // bool was_soloed = route.soloed();
2351 route.set_solo (true, this);
2353 _session->request_bounded_roll (region->position(), region->position() + region->length());
2355 /* XXX how to unset the solo state ? */
2358 /** Start an audition of the first selected region */
2360 Editor::play_edit_range ()
2362 framepos_t start, end;
2364 if (get_edit_op_range (start, end)) {
2365 _session->request_bounded_roll (start, end);
2370 Editor::play_selected_region ()
2372 framepos_t start = max_framepos;
2375 RegionSelection rs = get_regions_from_selection_and_entered ();
2381 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2382 if ((*i)->region()->position() < start) {
2383 start = (*i)->region()->position();
2385 if ((*i)->region()->last_frame() + 1 > end) {
2386 end = (*i)->region()->last_frame() + 1;
2390 _session->request_bounded_roll (start, end);
2394 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2396 _session->audition_region (region);
2400 Editor::region_from_selection ()
2402 if (clicked_axisview == 0) {
2406 if (selection->time.empty()) {
2410 framepos_t start = selection->time[clicked_selection].start;
2411 framepos_t end = selection->time[clicked_selection].end;
2413 TrackViewList tracks = get_tracks_for_range_action ();
2415 framepos_t selection_cnt = end - start + 1;
2417 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2418 boost::shared_ptr<Region> current;
2419 boost::shared_ptr<Playlist> pl;
2420 framepos_t internal_start;
2423 if ((pl = (*i)->playlist()) == 0) {
2427 if ((current = pl->top_region_at (start)) == 0) {
2431 internal_start = start - current->position();
2432 RegionFactory::region_name (new_name, current->name(), true);
2436 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2437 plist.add (ARDOUR::Properties::length, selection_cnt);
2438 plist.add (ARDOUR::Properties::name, new_name);
2439 plist.add (ARDOUR::Properties::layer, 0);
2441 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2446 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2448 if (selection->time.empty() || selection->tracks.empty()) {
2452 framepos_t start = selection->time[clicked_selection].start;
2453 framepos_t end = selection->time[clicked_selection].end;
2455 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2456 sort_track_selection (ts);
2458 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2459 boost::shared_ptr<Region> current;
2460 boost::shared_ptr<Playlist> playlist;
2461 framepos_t internal_start;
2464 if ((playlist = (*i)->playlist()) == 0) {
2468 if ((current = playlist->top_region_at(start)) == 0) {
2472 internal_start = start - current->position();
2473 RegionFactory::region_name (new_name, current->name(), true);
2477 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2478 plist.add (ARDOUR::Properties::length, end - start + 1);
2479 plist.add (ARDOUR::Properties::name, new_name);
2481 new_regions.push_back (RegionFactory::create (current, plist));
2486 Editor::split_multichannel_region ()
2488 RegionSelection rs = get_regions_from_selection_and_entered ();
2494 vector< boost::shared_ptr<Region> > v;
2496 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2497 (*x)->region()->separate_by_channel (*_session, v);
2502 Editor::new_region_from_selection ()
2504 region_from_selection ();
2505 cancel_selection ();
2509 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2511 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2512 case Evoral::OverlapNone:
2520 * - selected tracks, or if there are none...
2521 * - tracks containing selected regions, or if there are none...
2526 Editor::get_tracks_for_range_action () const
2530 if (selection->tracks.empty()) {
2532 /* use tracks with selected regions */
2534 RegionSelection rs = selection->regions;
2536 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2537 TimeAxisView* tv = &(*i)->get_time_axis_view();
2539 if (!t.contains (tv)) {
2545 /* no regions and no tracks: use all tracks */
2551 t = selection->tracks;
2554 return t.filter_to_unique_playlists();
2558 Editor::separate_regions_between (const TimeSelection& ts)
2560 bool in_command = false;
2561 boost::shared_ptr<Playlist> playlist;
2562 RegionSelection new_selection;
2564 TrackViewList tmptracks = get_tracks_for_range_action ();
2565 sort_track_selection (tmptracks);
2567 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2569 RouteTimeAxisView* rtv;
2571 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2573 if (rtv->is_track()) {
2575 /* no edits to destructive tracks */
2577 if (rtv->track()->destructive()) {
2581 if ((playlist = rtv->playlist()) != 0) {
2583 playlist->clear_changes ();
2585 /* XXX need to consider musical time selections here at some point */
2587 double speed = rtv->track()->speed();
2590 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2592 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2593 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2595 latest_regionviews.clear ();
2597 playlist->partition ((framepos_t)((*t).start * speed),
2598 (framepos_t)((*t).end * speed), false);
2602 if (!latest_regionviews.empty()) {
2604 rtv->view()->foreach_regionview (sigc::bind (
2605 sigc::ptr_fun (add_if_covered),
2606 &(*t), &new_selection));
2609 begin_reversible_command (_("separate"));
2613 /* pick up changes to existing regions */
2615 vector<Command*> cmds;
2616 playlist->rdiff (cmds);
2617 _session->add_commands (cmds);
2619 /* pick up changes to the playlist itself (adds/removes)
2622 _session->add_command(new StatefulDiffCommand (playlist));
2631 selection->set (new_selection);
2632 set_mouse_mode (MouseObject);
2634 commit_reversible_command ();
2638 struct PlaylistState {
2639 boost::shared_ptr<Playlist> playlist;
2643 /** Take tracks from get_tracks_for_range_action and cut any regions
2644 * on those tracks so that the tracks are empty over the time
2648 Editor::separate_region_from_selection ()
2650 /* preferentially use *all* ranges in the time selection if we're in range mode
2651 to allow discontiguous operation, since get_edit_op_range() currently
2652 returns a single range.
2655 if (mouse_mode == MouseRange && !selection->time.empty()) {
2657 separate_regions_between (selection->time);
2664 if (get_edit_op_range (start, end)) {
2666 AudioRange ar (start, end, 1);
2670 separate_regions_between (ts);
2676 Editor::separate_region_from_punch ()
2678 Location* loc = _session->locations()->auto_punch_location();
2680 separate_regions_using_location (*loc);
2685 Editor::separate_region_from_loop ()
2687 Location* loc = _session->locations()->auto_loop_location();
2689 separate_regions_using_location (*loc);
2694 Editor::separate_regions_using_location (Location& loc)
2696 if (loc.is_mark()) {
2700 AudioRange ar (loc.start(), loc.end(), 1);
2705 separate_regions_between (ts);
2708 /** Separate regions under the selected region */
2710 Editor::separate_under_selected_regions ()
2712 vector<PlaylistState> playlists;
2716 rs = get_regions_from_selection_and_entered();
2718 if (!_session || rs.empty()) {
2722 begin_reversible_command (_("separate region under"));
2724 list<boost::shared_ptr<Region> > regions_to_remove;
2726 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2727 // we can't just remove the region(s) in this loop because
2728 // this removes them from the RegionSelection, and they thus
2729 // disappear from underneath the iterator, and the ++i above
2730 // SEGVs in a puzzling fashion.
2732 // so, first iterate over the regions to be removed from rs and
2733 // add them to the regions_to_remove list, and then
2734 // iterate over the list to actually remove them.
2736 regions_to_remove.push_back ((*i)->region());
2739 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2741 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2744 // is this check necessary?
2748 vector<PlaylistState>::iterator i;
2750 //only take state if this is a new playlist.
2751 for (i = playlists.begin(); i != playlists.end(); ++i) {
2752 if ((*i).playlist == playlist) {
2757 if (i == playlists.end()) {
2759 PlaylistState before;
2760 before.playlist = playlist;
2761 before.before = &playlist->get_state();
2763 playlist->freeze ();
2764 playlists.push_back(before);
2767 //Partition on the region bounds
2768 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2770 //Re-add region that was just removed due to the partition operation
2771 playlist->add_region( (*rl), (*rl)->first_frame() );
2774 vector<PlaylistState>::iterator pl;
2776 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2777 (*pl).playlist->thaw ();
2778 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2781 commit_reversible_command ();
2785 Editor::crop_region_to_selection ()
2787 if (!selection->time.empty()) {
2789 crop_region_to (selection->time.start(), selection->time.end_frame());
2796 if (get_edit_op_range (start, end)) {
2797 crop_region_to (start, end);
2804 Editor::crop_region_to (framepos_t start, framepos_t end)
2806 vector<boost::shared_ptr<Playlist> > playlists;
2807 boost::shared_ptr<Playlist> playlist;
2810 if (selection->tracks.empty()) {
2811 ts = track_views.filter_to_unique_playlists();
2813 ts = selection->tracks.filter_to_unique_playlists ();
2816 sort_track_selection (ts);
2818 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2820 RouteTimeAxisView* rtv;
2822 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2824 boost::shared_ptr<Track> t = rtv->track();
2826 if (t != 0 && ! t->destructive()) {
2828 if ((playlist = rtv->playlist()) != 0) {
2829 playlists.push_back (playlist);
2835 if (playlists.empty()) {
2839 framepos_t the_start;
2843 begin_reversible_command (_("trim to selection"));
2845 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2847 boost::shared_ptr<Region> region;
2851 if ((region = (*i)->top_region_at(the_start)) == 0) {
2855 /* now adjust lengths to that we do the right thing
2856 if the selection extends beyond the region
2859 the_start = max (the_start, (framepos_t) region->position());
2860 if (max_framepos - the_start < region->length()) {
2861 the_end = the_start + region->length() - 1;
2863 the_end = max_framepos;
2865 the_end = min (end, the_end);
2866 cnt = the_end - the_start + 1;
2868 region->clear_changes ();
2869 region->trim_to (the_start, cnt);
2870 _session->add_command (new StatefulDiffCommand (region));
2873 commit_reversible_command ();
2877 Editor::region_fill_track ()
2879 RegionSelection rs = get_regions_from_selection_and_entered ();
2881 if (!_session || rs.empty()) {
2885 framepos_t const end = _session->current_end_frame ();
2887 begin_reversible_command (Operations::region_fill);
2889 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2891 boost::shared_ptr<Region> region ((*i)->region());
2893 boost::shared_ptr<Playlist> pl = region->playlist();
2895 if (end <= region->last_frame()) {
2899 double times = (double) (end - region->last_frame()) / (double) region->length();
2905 pl->clear_changes ();
2906 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2907 _session->add_command (new StatefulDiffCommand (pl));
2910 commit_reversible_command ();
2914 Editor::region_fill_selection ()
2916 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2920 if (selection->time.empty()) {
2924 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2929 framepos_t start = selection->time[clicked_selection].start;
2930 framepos_t end = selection->time[clicked_selection].end;
2932 boost::shared_ptr<Playlist> playlist;
2934 if (selection->tracks.empty()) {
2938 framepos_t selection_length = end - start;
2939 float times = (float)selection_length / region->length();
2941 begin_reversible_command (Operations::fill_selection);
2943 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2945 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
2947 if ((playlist = (*i)->playlist()) == 0) {
2951 playlist->clear_changes ();
2952 playlist->add_region (RegionFactory::create (region, true), start, times);
2953 _session->add_command (new StatefulDiffCommand (playlist));
2956 commit_reversible_command ();
2960 Editor::set_region_sync_position ()
2962 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
2966 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
2968 bool in_command = false;
2970 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2972 if (!(*r)->region()->covers (where)) {
2976 boost::shared_ptr<Region> region ((*r)->region());
2979 begin_reversible_command (_("set sync point"));
2983 region->clear_changes ();
2984 region->set_sync_position (where);
2985 _session->add_command(new StatefulDiffCommand (region));
2989 commit_reversible_command ();
2993 /** Remove the sync positions of the selection */
2995 Editor::remove_region_sync ()
2997 RegionSelection rs = get_regions_from_selection_and_entered ();
3003 begin_reversible_command (_("remove region sync"));
3005 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3007 (*i)->region()->clear_changes ();
3008 (*i)->region()->clear_sync_position ();
3009 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3012 commit_reversible_command ();
3016 Editor::naturalize_region ()
3018 RegionSelection rs = get_regions_from_selection_and_entered ();
3024 if (rs.size() > 1) {
3025 begin_reversible_command (_("move regions to original position"));
3027 begin_reversible_command (_("move region to original position"));
3030 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3031 (*i)->region()->clear_changes ();
3032 (*i)->region()->move_to_natural_position ();
3033 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3036 commit_reversible_command ();
3040 Editor::align_regions (RegionPoint what)
3042 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3048 begin_reversible_command (_("align selection"));
3050 framepos_t const position = get_preferred_edit_position ();
3052 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3053 align_region_internal ((*i)->region(), what, position);
3056 commit_reversible_command ();
3059 struct RegionSortByTime {
3060 bool operator() (const RegionView* a, const RegionView* b) {
3061 return a->region()->position() < b->region()->position();
3066 Editor::align_regions_relative (RegionPoint point)
3068 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3074 framepos_t const position = get_preferred_edit_position ();
3076 framepos_t distance = 0;
3080 list<RegionView*> sorted;
3081 rs.by_position (sorted);
3083 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3088 if (position > r->position()) {
3089 distance = position - r->position();
3091 distance = r->position() - position;
3097 if (position > r->last_frame()) {
3098 distance = position - r->last_frame();
3099 pos = r->position() + distance;
3101 distance = r->last_frame() - position;
3102 pos = r->position() - distance;
3108 pos = r->adjust_to_sync (position);
3109 if (pos > r->position()) {
3110 distance = pos - r->position();
3112 distance = r->position() - pos;
3118 if (pos == r->position()) {
3122 begin_reversible_command (_("align selection (relative)"));
3124 /* move first one specially */
3126 r->clear_changes ();
3127 r->set_position (pos);
3128 _session->add_command(new StatefulDiffCommand (r));
3130 /* move rest by the same amount */
3134 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3136 boost::shared_ptr<Region> region ((*i)->region());
3138 region->clear_changes ();
3141 region->set_position (region->position() + distance);
3143 region->set_position (region->position() - distance);
3146 _session->add_command(new StatefulDiffCommand (region));
3150 commit_reversible_command ();
3154 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3156 begin_reversible_command (_("align region"));
3157 align_region_internal (region, point, position);
3158 commit_reversible_command ();
3162 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3164 region->clear_changes ();
3168 region->set_position (region->adjust_to_sync (position));
3172 if (position > region->length()) {
3173 region->set_position (position - region->length());
3178 region->set_position (position);
3182 _session->add_command(new StatefulDiffCommand (region));
3186 Editor::trim_region_front ()
3192 Editor::trim_region_back ()
3194 trim_region (false);
3198 Editor::trim_region (bool front)
3200 framepos_t where = get_preferred_edit_position();
3201 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3207 begin_reversible_command (front ? _("trim front") : _("trim back"));
3209 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3210 if (!(*i)->region()->locked()) {
3212 (*i)->region()->clear_changes ();
3215 (*i)->region()->trim_front (where);
3217 (*i)->region()->trim_end (where);
3220 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3224 commit_reversible_command ();
3227 /** Trim the end of the selected regions to the position of the edit cursor */
3229 Editor::trim_region_to_loop ()
3231 Location* loc = _session->locations()->auto_loop_location();
3235 trim_region_to_location (*loc, _("trim to loop"));
3239 Editor::trim_region_to_punch ()
3241 Location* loc = _session->locations()->auto_punch_location();
3245 trim_region_to_location (*loc, _("trim to punch"));
3249 Editor::trim_region_to_location (const Location& loc, const char* str)
3251 RegionSelection rs = get_regions_from_selection_and_entered ();
3253 begin_reversible_command (str);
3255 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3256 RegionView* rv = (*x);
3258 /* require region to span proposed trim */
3259 switch (rv->region()->coverage (loc.start(), loc.end())) {
3260 case Evoral::OverlapInternal:
3266 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3275 if (tav->track() != 0) {
3276 speed = tav->track()->speed();
3279 start = session_frame_to_track_frame (loc.start(), speed);
3280 end = session_frame_to_track_frame (loc.end(), speed);
3282 rv->region()->clear_changes ();
3283 rv->region()->trim_to (start, (end - start));
3284 _session->add_command(new StatefulDiffCommand (rv->region()));
3287 commit_reversible_command ();
3291 Editor::trim_region_to_previous_region_end ()
3293 return trim_to_region(false);
3297 Editor::trim_region_to_next_region_start ()
3299 return trim_to_region(true);
3303 Editor::trim_to_region(bool forward)
3305 RegionSelection rs = get_regions_from_selection_and_entered ();
3307 begin_reversible_command (_("trim to region"));
3309 boost::shared_ptr<Region> next_region;
3311 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3313 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3319 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3327 if (atav->track() != 0) {
3328 speed = atav->track()->speed();
3332 boost::shared_ptr<Region> region = arv->region();
3333 boost::shared_ptr<Playlist> playlist (region->playlist());
3335 region->clear_changes ();
3339 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3345 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3346 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3350 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3356 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3358 arv->region_changed (ARDOUR::bounds_change);
3361 _session->add_command(new StatefulDiffCommand (region));
3364 commit_reversible_command ();
3368 Editor::unfreeze_route ()
3370 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3374 clicked_routeview->track()->unfreeze ();
3378 Editor::_freeze_thread (void* arg)
3380 return static_cast<Editor*>(arg)->freeze_thread ();
3384 Editor::freeze_thread ()
3386 /* create event pool because we may need to talk to the session */
3387 SessionEvent::create_per_thread_pool ("freeze events", 64);
3388 /* create per-thread buffers for process() tree to use */
3389 current_interthread_info->process_thread.init ();
3390 current_interthread_info->process_thread.get_buffers ();
3391 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3392 current_interthread_info->done = true;
3393 current_interthread_info->process_thread.drop_buffers();
3398 Editor::freeze_route ()
3404 /* stop transport before we start. this is important */
3406 _session->request_transport_speed (0.0);
3408 /* wait for just a little while, because the above call is asynchronous */
3412 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3416 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3418 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3419 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3421 d.set_title (_("Cannot freeze"));
3426 if (clicked_routeview->track()->has_external_redirects()) {
3427 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"
3428 "Freezing will only process the signal as far as the first send/insert/return."),
3429 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3431 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3432 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3433 d.set_title (_("Freeze Limits"));
3435 int response = d.run ();
3438 case Gtk::RESPONSE_CANCEL:
3445 InterThreadInfo itt;
3446 current_interthread_info = &itt;
3448 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3450 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3452 set_canvas_cursor (_cursors->wait);
3454 while (!itt.done && !itt.cancel) {
3455 gtk_main_iteration ();
3458 current_interthread_info = 0;
3459 set_canvas_cursor (current_canvas_cursor);
3463 Editor::bounce_range_selection (bool replace, bool enable_processing)
3465 if (selection->time.empty()) {
3469 TrackSelection views = selection->tracks;
3471 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3473 if (enable_processing) {
3475 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3477 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3479 _("You can't perform this operation because the processing of the signal "
3480 "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3481 "You can do this without processing, which is a different operation.")
3483 d.set_title (_("Cannot bounce"));
3490 framepos_t start = selection->time[clicked_selection].start;
3491 framepos_t end = selection->time[clicked_selection].end;
3492 framepos_t cnt = end - start + 1;
3494 begin_reversible_command (_("bounce range"));
3496 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3498 RouteTimeAxisView* rtv;
3500 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3504 boost::shared_ptr<Playlist> playlist;
3506 if ((playlist = rtv->playlist()) == 0) {
3510 InterThreadInfo itt;
3512 playlist->clear_changes ();
3513 playlist->clear_owned_changes ();
3515 boost::shared_ptr<Region> r;
3517 if (enable_processing) {
3518 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3520 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3528 list<AudioRange> ranges;
3529 ranges.push_back (AudioRange (start, start+cnt, 0));
3530 playlist->cut (ranges); // discard result
3531 playlist->add_region (r, start);
3534 vector<Command*> cmds;
3535 playlist->rdiff (cmds);
3536 _session->add_commands (cmds);
3538 _session->add_command (new StatefulDiffCommand (playlist));
3541 commit_reversible_command ();
3544 /** Delete selected regions, automation points or a time range */
3551 /** Cut selected regions, automation points or a time range */
3558 /** Copy selected regions, automation points or a time range */
3566 /** @return true if a Cut, Copy or Clear is possible */
3568 Editor::can_cut_copy () const
3570 switch (current_mouse_mode()) {
3573 if (!selection->regions.empty() || !selection->points.empty()) {
3579 if (!selection->time.empty()) {
3592 /** Cut, copy or clear selected regions, automation points or a time range.
3593 * @param op Operation (Cut, Copy or Clear)
3596 Editor::cut_copy (CutCopyOp op)
3598 /* only cancel selection if cut/copy is successful.*/
3604 opname = _("delete");
3613 opname = _("clear");
3617 /* if we're deleting something, and the mouse is still pressed,
3618 the thing we started a drag for will be gone when we release
3619 the mouse button(s). avoid this. see part 2 at the end of
3623 if (op == Delete || op == Cut || op == Clear) {
3624 if (_drags->active ()) {
3629 cut_buffer->clear ();
3631 if (entered_marker) {
3633 /* cut/delete op while pointing at a marker */
3636 Location* loc = find_location_from_marker (entered_marker, ignored);
3638 if (_session && loc) {
3639 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3646 if (internal_editing()) {
3648 switch (current_mouse_mode()) {
3661 /* we only want to cut regions if some are selected */
3663 if (doing_object_stuff()) {
3664 rs = get_regions_from_selection ();
3665 if (!rs.empty() || !selection->points.empty()) {
3667 begin_reversible_command (opname + _(" objects"));
3670 cut_copy_regions (op, rs);
3672 if (op == Cut || op == Delete) {
3673 selection->clear_regions ();
3677 if (!selection->points.empty()) {
3678 cut_copy_points (op);
3680 if (op == Cut || op == Delete) {
3681 selection->clear_points ();
3684 commit_reversible_command ();
3687 if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3688 /* don't cause suprises */
3693 if (doing_range_stuff()) {
3694 if (selection->time.empty()) {
3695 framepos_t start, end;
3696 if (!get_edit_op_range (start, end)) {
3699 selection->set (start, end);
3702 begin_reversible_command (opname + _(" range"));
3703 cut_copy_ranges (op);
3704 commit_reversible_command ();
3706 if (op == Cut || op == Delete) {
3707 selection->clear_time ();
3713 if (op == Delete || op == Cut || op == Clear) {
3718 struct AutomationRecord {
3719 AutomationRecord () : state (0) {}
3720 AutomationRecord (XMLNode* s) : state (s) {}
3722 XMLNode* state; ///< state before any operation
3723 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3726 /** Cut, copy or clear selected automation points.
3727 * @param op Operation (Cut, Copy or Clear)
3730 Editor::cut_copy_points (CutCopyOp op)
3732 if (selection->points.empty ()) {
3736 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3737 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3739 /* Keep a record of the AutomationLists that we end up using in this operation */
3740 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3743 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3744 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3745 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3746 if (lists.find (al) == lists.end ()) {
3747 /* We haven't seen this list yet, so make a record for it. This includes
3748 taking a copy of its current state, in case this is needed for undo later.
3750 lists[al] = AutomationRecord (&al->get_state ());
3754 if (op == Cut || op == Copy) {
3755 /* This operation will involve putting things in the cut buffer, so create an empty
3756 ControlList for each of our source lists to put the cut buffer data in.
3758 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3759 i->second.copy = i->first->create (i->first->parameter ());
3762 /* Add all selected points to the relevant copy ControlLists */
3763 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3764 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3765 AutomationList::const_iterator j = (*i)->model ();
3766 lists[al].copy->add ((*j)->when, (*j)->value);
3769 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3770 /* Correct this copy list so that it starts at time 0 */
3771 double const start = i->second.copy->front()->when;
3772 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3773 (*j)->when -= start;
3776 /* And add it to the cut buffer */
3777 cut_buffer->add (i->second.copy);
3781 if (op == Delete || op == Cut) {
3782 /* This operation needs to remove things from the main AutomationList, so do that now */
3784 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3785 i->first->freeze ();
3788 /* Remove each selected point from its AutomationList */
3789 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3790 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3791 al->erase ((*i)->model ());
3794 /* Thaw the lists and add undo records for them */
3795 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3796 boost::shared_ptr<AutomationList> al = i->first;
3798 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3803 /** Cut, copy or clear selected automation points.
3804 * @param op Operation (Cut, Copy or Clear)
3807 Editor::cut_copy_midi (CutCopyOp op)
3809 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3810 MidiRegionView* mrv = *i;
3811 mrv->cut_copy_clear (op);
3817 struct lt_playlist {
3818 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3819 return a.playlist < b.playlist;
3823 struct PlaylistMapping {
3825 boost::shared_ptr<Playlist> pl;
3827 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3830 /** Remove `clicked_regionview' */
3832 Editor::remove_clicked_region ()
3834 if (clicked_routeview == 0 || clicked_regionview == 0) {
3838 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3840 begin_reversible_command (_("remove region"));
3841 playlist->clear_changes ();
3842 playlist->clear_owned_changes ();
3843 playlist->remove_region (clicked_regionview->region());
3845 /* We might have removed regions, which alters other regions' layering_index,
3846 so we need to do a recursive diff here.
3848 vector<Command*> cmds;
3849 playlist->rdiff (cmds);
3850 _session->add_commands (cmds);
3852 _session->add_command(new StatefulDiffCommand (playlist));
3853 commit_reversible_command ();
3857 /** Remove the selected regions */
3859 Editor::remove_selected_regions ()
3861 RegionSelection rs = get_regions_from_selection_and_entered ();
3863 if (!_session || rs.empty()) {
3867 begin_reversible_command (_("remove region"));
3869 list<boost::shared_ptr<Region> > regions_to_remove;
3871 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3872 // we can't just remove the region(s) in this loop because
3873 // this removes them from the RegionSelection, and they thus
3874 // disappear from underneath the iterator, and the ++i above
3875 // SEGVs in a puzzling fashion.
3877 // so, first iterate over the regions to be removed from rs and
3878 // add them to the regions_to_remove list, and then
3879 // iterate over the list to actually remove them.
3881 regions_to_remove.push_back ((*i)->region());
3884 vector<boost::shared_ptr<Playlist> > playlists;
3886 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3888 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3891 // is this check necessary?
3895 /* get_regions_from_selection_and_entered() guarantees that
3896 the playlists involved are unique, so there is no need
3900 playlists.push_back (playlist);
3902 playlist->clear_changes ();
3903 playlist->clear_owned_changes ();
3904 playlist->freeze ();
3905 playlist->remove_region (*rl);
3908 vector<boost::shared_ptr<Playlist> >::iterator pl;
3910 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3913 /* We might have removed regions, which alters other regions' layering_index,
3914 so we need to do a recursive diff here.
3916 vector<Command*> cmds;
3917 (*pl)->rdiff (cmds);
3918 _session->add_commands (cmds);
3920 _session->add_command(new StatefulDiffCommand (*pl));
3923 commit_reversible_command ();
3926 /** Cut, copy or clear selected regions.
3927 * @param op Operation (Cut, Copy or Clear)
3930 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3932 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3933 a map when we want ordered access to both elements. i think.
3936 vector<PlaylistMapping> pmap;
3938 framepos_t first_position = max_framepos;
3940 typedef set<boost::shared_ptr<Playlist> > FreezeList;
3941 FreezeList freezelist;
3943 /* get ordering correct before we cut/copy */
3945 rs.sort_by_position_and_track ();
3947 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3949 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3951 if (op == Cut || op == Clear || op == Delete) {
3952 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3955 FreezeList::iterator fl;
3957 // only take state if this is a new playlist.
3958 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3964 if (fl == freezelist.end()) {
3965 pl->clear_changes();
3966 pl->clear_owned_changes ();
3968 freezelist.insert (pl);
3973 TimeAxisView* tv = &(*x)->get_time_axis_view();
3974 vector<PlaylistMapping>::iterator z;
3976 for (z = pmap.begin(); z != pmap.end(); ++z) {
3977 if ((*z).tv == tv) {
3982 if (z == pmap.end()) {
3983 pmap.push_back (PlaylistMapping (tv));
3987 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3989 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3992 /* region not yet associated with a playlist (e.g. unfinished
3999 TimeAxisView& tv = (*x)->get_time_axis_view();
4000 boost::shared_ptr<Playlist> npl;
4001 RegionSelection::iterator tmp;
4008 vector<PlaylistMapping>::iterator z;
4010 for (z = pmap.begin(); z != pmap.end(); ++z) {
4011 if ((*z).tv == &tv) {
4016 assert (z != pmap.end());
4019 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4027 boost::shared_ptr<Region> r = (*x)->region();
4028 boost::shared_ptr<Region> _xx;
4034 pl->remove_region (r);
4038 _xx = RegionFactory::create (r);
4039 npl->add_region (_xx, r->position() - first_position);
4040 pl->remove_region (r);
4044 /* copy region before adding, so we're not putting same object into two different playlists */
4045 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4049 pl->remove_region (r);
4058 list<boost::shared_ptr<Playlist> > foo;
4060 /* the pmap is in the same order as the tracks in which selected regions occured */
4062 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4065 foo.push_back ((*i).pl);
4070 cut_buffer->set (foo);
4074 _last_cut_copy_source_track = 0;
4076 _last_cut_copy_source_track = pmap.front().tv;
4080 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4083 /* We might have removed regions, which alters other regions' layering_index,
4084 so we need to do a recursive diff here.
4086 vector<Command*> cmds;
4087 (*pl)->rdiff (cmds);
4088 _session->add_commands (cmds);
4090 _session->add_command (new StatefulDiffCommand (*pl));
4095 Editor::cut_copy_ranges (CutCopyOp op)
4097 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4099 /* Sort the track selection now, so that it if is used, the playlists
4100 selected by the calls below to cut_copy_clear are in the order that
4101 their tracks appear in the editor. This makes things like paste
4102 of ranges work properly.
4105 sort_track_selection (ts);
4108 if (!entered_track) {
4111 ts.push_back (entered_track);
4114 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4115 (*i)->cut_copy_clear (*selection, op);
4120 Editor::paste (float times, bool from_context)
4122 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4124 paste_internal (get_preferred_edit_position (false, from_context), times);
4128 Editor::mouse_paste ()
4133 if (!mouse_frame (where, ignored)) {
4138 paste_internal (where, 1);
4142 Editor::paste_internal (framepos_t position, float times)
4144 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4146 if (internal_editing()) {
4147 if (cut_buffer->midi_notes.empty()) {
4151 if (cut_buffer->empty()) {
4156 if (position == max_framepos) {
4157 position = get_preferred_edit_position();
4158 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4162 TrackViewList::iterator i;
4165 /* get everything in the correct order */
4167 if (!selection->tracks.empty()) {
4168 /* there are some selected tracks, so paste to them */
4169 ts = selection->tracks.filter_to_unique_playlists ();
4170 sort_track_selection (ts);
4171 } else if (_last_cut_copy_source_track) {
4172 /* otherwise paste to the track that the cut/copy came from;
4173 see discussion in mantis #3333.
4175 ts.push_back (_last_cut_copy_source_track);
4178 if (internal_editing ()) {
4180 /* undo/redo is handled by individual tracks/regions */
4182 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4185 RegionSelection::iterator r;
4186 MidiNoteSelection::iterator cb;
4188 get_regions_at (rs, position, ts);
4190 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4191 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4192 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4194 mrv->paste (position, times, **cb);
4202 /* we do redo (do you do voodoo?) */
4204 begin_reversible_command (Operations::paste);
4206 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4207 (*i)->paste (position, times, *cut_buffer, nth);
4210 commit_reversible_command ();
4215 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4217 boost::shared_ptr<Playlist> playlist;
4218 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4219 RegionSelection foo;
4221 framepos_t const start_frame = regions.start ();
4222 framepos_t const end_frame = regions.end_frame ();
4224 begin_reversible_command (Operations::duplicate_region);
4226 selection->clear_regions ();
4228 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4230 boost::shared_ptr<Region> r ((*i)->region());
4232 TimeAxisView& tv = (*i)->get_time_axis_view();
4233 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4234 latest_regionviews.clear ();
4235 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4237 playlist = (*i)->region()->playlist();
4238 playlist->clear_changes ();
4239 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4240 _session->add_command(new StatefulDiffCommand (playlist));
4244 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4247 commit_reversible_command ();
4250 selection->set (foo);
4255 Editor::duplicate_selection (float times)
4257 if (selection->time.empty() || selection->tracks.empty()) {
4261 boost::shared_ptr<Playlist> playlist;
4262 vector<boost::shared_ptr<Region> > new_regions;
4263 vector<boost::shared_ptr<Region> >::iterator ri;
4265 create_region_from_selection (new_regions);
4267 if (new_regions.empty()) {
4271 begin_reversible_command (_("duplicate selection"));
4273 ri = new_regions.begin();
4275 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4277 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4278 if ((playlist = (*i)->playlist()) == 0) {
4281 playlist->clear_changes ();
4282 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4283 _session->add_command (new StatefulDiffCommand (playlist));
4286 if (ri == new_regions.end()) {
4291 commit_reversible_command ();
4294 /** Reset all selected points to the relevant default value */
4296 Editor::reset_point_selection ()
4298 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4299 ARDOUR::AutomationList::iterator j = (*i)->model ();
4300 (*j)->value = (*i)->line().the_list()->default_value ();
4305 Editor::center_playhead ()
4307 float page = _canvas_width * frames_per_unit;
4308 center_screen_internal (playhead_cursor->current_frame, page);
4312 Editor::center_edit_point ()
4314 float page = _canvas_width * frames_per_unit;
4315 center_screen_internal (get_preferred_edit_position(), page);
4318 /** Caller must begin and commit a reversible command */
4320 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4322 playlist->clear_changes ();
4324 _session->add_command (new StatefulDiffCommand (playlist));
4328 Editor::nudge_track (bool use_edit, bool forwards)
4330 boost::shared_ptr<Playlist> playlist;
4331 framepos_t distance;
4332 framepos_t next_distance;
4336 start = get_preferred_edit_position();
4341 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4345 if (selection->tracks.empty()) {
4349 begin_reversible_command (_("nudge track"));
4351 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4353 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4355 if ((playlist = (*i)->playlist()) == 0) {
4359 playlist->clear_changes ();
4360 playlist->clear_owned_changes ();
4362 playlist->nudge_after (start, distance, forwards);
4364 vector<Command*> cmds;
4366 playlist->rdiff (cmds);
4367 _session->add_commands (cmds);
4369 _session->add_command (new StatefulDiffCommand (playlist));
4372 commit_reversible_command ();
4376 Editor::remove_last_capture ()
4378 vector<string> choices;
4385 if (Config->get_verify_remove_last_capture()) {
4386 prompt = _("Do you really want to destroy the last capture?"
4387 "\n(This is destructive and cannot be undone)");
4389 choices.push_back (_("No, do nothing."));
4390 choices.push_back (_("Yes, destroy it."));
4392 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4394 if (prompter.run () == 1) {
4395 _session->remove_last_capture ();
4396 _regions->redisplay ();
4400 _session->remove_last_capture();
4401 _regions->redisplay ();
4406 Editor::normalize_region ()
4412 RegionSelection rs = get_regions_from_selection_and_entered ();
4418 NormalizeDialog dialog (rs.size() > 1);
4420 if (dialog.run () == RESPONSE_CANCEL) {
4424 set_canvas_cursor (_cursors->wait);
4427 /* XXX: should really only count audio regions here */
4428 int const regions = rs.size ();
4430 /* Make a list of the selected audio regions' maximum amplitudes, and also
4431 obtain the maximum amplitude of them all.
4433 list<double> max_amps;
4435 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4436 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4438 dialog.descend (1.0 / regions);
4439 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4442 /* the user cancelled the operation */
4443 set_canvas_cursor (current_canvas_cursor);
4447 max_amps.push_back (a);
4448 max_amp = max (max_amp, a);
4453 begin_reversible_command (_("normalize"));
4455 list<double>::const_iterator a = max_amps.begin ();
4457 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4458 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4463 arv->region()->clear_changes ();
4465 double const amp = dialog.normalize_individually() ? *a : max_amp;
4467 arv->audio_region()->normalize (amp, dialog.target ());
4468 _session->add_command (new StatefulDiffCommand (arv->region()));
4473 commit_reversible_command ();
4474 set_canvas_cursor (current_canvas_cursor);
4479 Editor::reset_region_scale_amplitude ()
4485 RegionSelection rs = get_regions_from_selection_and_entered ();
4491 begin_reversible_command ("reset gain");
4493 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4494 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4497 arv->region()->clear_changes ();
4498 arv->audio_region()->set_scale_amplitude (1.0f);
4499 _session->add_command (new StatefulDiffCommand (arv->region()));
4502 commit_reversible_command ();
4506 Editor::adjust_region_gain (bool up)
4508 RegionSelection rs = get_regions_from_selection_and_entered ();
4510 if (!_session || rs.empty()) {
4514 begin_reversible_command ("adjust region gain");
4516 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4517 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4522 arv->region()->clear_changes ();
4524 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4532 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4533 _session->add_command (new StatefulDiffCommand (arv->region()));
4536 commit_reversible_command ();
4541 Editor::reverse_region ()
4547 Reverse rev (*_session);
4548 apply_filter (rev, _("reverse regions"));
4552 Editor::strip_region_silence ()
4558 RegionSelection rs = get_regions_from_selection_and_entered ();
4564 std::list<RegionView*> audio_only;
4566 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4567 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4569 audio_only.push_back (arv);
4573 StripSilenceDialog d (_session, audio_only);
4574 int const r = d.run ();
4578 if (r == Gtk::RESPONSE_OK) {
4579 ARDOUR::AudioIntervalMap silences;
4580 d.silences (silences);
4581 StripSilence s (*_session, silences, d.fade_length());
4582 apply_filter (s, _("strip silence"), &d);
4587 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4589 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4590 mrv.selection_as_notelist (selected, true);
4592 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4593 v.push_back (selected);
4595 framepos_t pos_frames = mrv.midi_region()->position();
4596 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4598 return op (mrv.midi_region()->model(), pos_beats, v);
4602 Editor::apply_midi_note_edit_op (MidiOperator& op)
4606 RegionSelection rs = get_regions_from_selection_and_entered ();
4612 begin_reversible_command (op.name ());
4614 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4615 RegionSelection::iterator tmp = r;
4618 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4621 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4624 _session->add_command (cmd);
4631 commit_reversible_command ();
4635 Editor::fork_region ()
4637 RegionSelection rs = get_regions_from_selection_and_entered ();
4643 begin_reversible_command (_("Fork Region(s)"));
4645 set_canvas_cursor (_cursors->wait);
4648 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4649 RegionSelection::iterator tmp = r;
4652 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4655 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4656 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4658 playlist->clear_changes ();
4659 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4660 _session->add_command(new StatefulDiffCommand (playlist));
4666 commit_reversible_command ();
4668 set_canvas_cursor (current_canvas_cursor);
4672 Editor::quantize_region ()
4674 int selected_midi_region_cnt = 0;
4680 RegionSelection rs = get_regions_from_selection_and_entered ();
4686 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4687 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4689 selected_midi_region_cnt++;
4693 if (selected_midi_region_cnt == 0) {
4697 QuantizeDialog* qd = new QuantizeDialog (*this);
4700 const int r = qd->run ();
4703 if (r == Gtk::RESPONSE_OK) {
4704 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4705 qd->start_grid_size(), qd->end_grid_size(),
4706 qd->strength(), qd->swing(), qd->threshold());
4708 apply_midi_note_edit_op (quant);
4713 Editor::insert_patch_change (bool from_context)
4715 RegionSelection rs = get_regions_from_selection_and_entered ();
4721 const framepos_t p = get_preferred_edit_position (false, from_context);
4723 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4724 PatchChangeDialog d (0, _session, empty, Gtk::Stock::ADD);
4726 if (d.run() == RESPONSE_CANCEL) {
4730 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4731 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4733 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4734 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4741 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4743 RegionSelection rs = get_regions_from_selection_and_entered ();
4749 begin_reversible_command (command);
4751 set_canvas_cursor (_cursors->wait);
4755 int const N = rs.size ();
4757 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4758 RegionSelection::iterator tmp = r;
4761 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4763 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4766 progress->descend (1.0 / N);
4769 if (arv->audio_region()->apply (filter, progress) == 0) {
4771 playlist->clear_changes ();
4772 playlist->clear_owned_changes ();
4774 if (filter.results.empty ()) {
4776 /* no regions returned; remove the old one */
4777 playlist->remove_region (arv->region ());
4781 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4783 /* first region replaces the old one */
4784 playlist->replace_region (arv->region(), *res, (*res)->position());
4788 while (res != filter.results.end()) {
4789 playlist->add_region (*res, (*res)->position());
4795 /* We might have removed regions, which alters other regions' layering_index,
4796 so we need to do a recursive diff here.
4798 vector<Command*> cmds;
4799 playlist->rdiff (cmds);
4800 _session->add_commands (cmds);
4802 _session->add_command(new StatefulDiffCommand (playlist));
4808 progress->ascend ();
4816 commit_reversible_command ();
4819 set_canvas_cursor (current_canvas_cursor);
4823 Editor::external_edit_region ()
4829 Editor::reset_region_gain_envelopes ()
4831 RegionSelection rs = get_regions_from_selection_and_entered ();
4833 if (!_session || rs.empty()) {
4837 _session->begin_reversible_command (_("reset region gain"));
4839 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4840 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4842 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4843 XMLNode& before (alist->get_state());
4845 arv->audio_region()->set_default_envelope ();
4846 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4850 _session->commit_reversible_command ();
4854 Editor::set_region_gain_visibility (RegionView* rv)
4856 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4858 arv->update_envelope_visibility();
4863 Editor::set_gain_envelope_visibility ()
4869 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4870 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4872 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4878 Editor::toggle_gain_envelope_active ()
4880 if (_ignore_region_action) {
4884 RegionSelection rs = get_regions_from_selection_and_entered ();
4886 if (!_session || rs.empty()) {
4890 _session->begin_reversible_command (_("region gain envelope active"));
4892 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4893 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4895 arv->region()->clear_changes ();
4896 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4897 _session->add_command (new StatefulDiffCommand (arv->region()));
4901 _session->commit_reversible_command ();
4905 Editor::toggle_region_lock ()
4907 if (_ignore_region_action) {
4911 RegionSelection rs = get_regions_from_selection_and_entered ();
4913 if (!_session || rs.empty()) {
4917 _session->begin_reversible_command (_("toggle region lock"));
4919 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4920 (*i)->region()->clear_changes ();
4921 (*i)->region()->set_locked (!(*i)->region()->locked());
4922 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4925 _session->commit_reversible_command ();
4929 Editor::toggle_region_lock_style ()
4931 if (_ignore_region_action) {
4935 RegionSelection rs = get_regions_from_selection_and_entered ();
4937 if (!_session || rs.empty()) {
4941 _session->begin_reversible_command (_("region lock style"));
4943 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4944 (*i)->region()->clear_changes ();
4945 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4946 (*i)->region()->set_position_lock_style (ns);
4947 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4950 _session->commit_reversible_command ();
4954 Editor::toggle_opaque_region ()
4956 if (_ignore_region_action) {
4960 RegionSelection rs = get_regions_from_selection_and_entered ();
4962 if (!_session || rs.empty()) {
4966 _session->begin_reversible_command (_("change region opacity"));
4968 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4969 (*i)->region()->clear_changes ();
4970 (*i)->region()->set_opaque (!(*i)->region()->opaque());
4971 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4974 _session->commit_reversible_command ();
4978 Editor::toggle_record_enable ()
4980 bool new_state = false;
4982 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4983 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
4986 if (!rtav->is_track())
4990 new_state = !rtav->track()->record_enabled();
4994 rtav->track()->set_record_enabled (new_state, this);
4999 Editor::toggle_solo ()
5001 bool new_state = false;
5003 boost::shared_ptr<RouteList> rl (new RouteList);
5005 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5006 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5013 new_state = !rtav->route()->soloed ();
5017 rl->push_back (rtav->route());
5020 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5024 Editor::toggle_mute ()
5026 bool new_state = false;
5028 boost::shared_ptr<RouteList> rl (new RouteList);
5030 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5031 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5038 new_state = !rtav->route()->muted();
5042 rl->push_back (rtav->route());
5045 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5049 Editor::toggle_solo_isolate ()
5054 Editor::set_fade_length (bool in)
5056 RegionSelection rs = get_regions_from_selection_and_entered ();
5062 /* we need a region to measure the offset from the start */
5064 RegionView* rv = rs.front ();
5066 framepos_t pos = get_preferred_edit_position();
5070 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5071 /* edit point is outside the relevant region */
5076 if (pos <= rv->region()->position()) {
5080 len = pos - rv->region()->position();
5081 cmd = _("set fade in length");
5083 if (pos >= rv->region()->last_frame()) {
5087 len = rv->region()->last_frame() - pos;
5088 cmd = _("set fade out length");
5091 begin_reversible_command (cmd);
5093 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5094 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5100 boost::shared_ptr<AutomationList> alist;
5102 alist = tmp->audio_region()->fade_in();
5104 alist = tmp->audio_region()->fade_out();
5107 XMLNode &before = alist->get_state();
5110 tmp->audio_region()->set_fade_in_length (len);
5111 tmp->audio_region()->set_fade_in_active (true);
5113 tmp->audio_region()->set_fade_out_length (len);
5114 tmp->audio_region()->set_fade_out_active (true);
5117 XMLNode &after = alist->get_state();
5118 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5121 commit_reversible_command ();
5125 Editor::set_fade_in_shape (FadeShape shape)
5127 RegionSelection rs = get_regions_from_selection_and_entered ();
5133 begin_reversible_command (_("set fade in shape"));
5135 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5136 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5142 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5143 XMLNode &before = alist->get_state();
5145 tmp->audio_region()->set_fade_in_shape (shape);
5147 XMLNode &after = alist->get_state();
5148 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5151 commit_reversible_command ();
5156 Editor::set_fade_out_shape (FadeShape shape)
5158 RegionSelection rs = get_regions_from_selection_and_entered ();
5164 begin_reversible_command (_("set fade out shape"));
5166 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5167 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5173 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5174 XMLNode &before = alist->get_state();
5176 tmp->audio_region()->set_fade_out_shape (shape);
5178 XMLNode &after = alist->get_state();
5179 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5182 commit_reversible_command ();
5186 Editor::set_fade_in_active (bool yn)
5188 RegionSelection rs = get_regions_from_selection_and_entered ();
5194 begin_reversible_command (_("set fade in active"));
5196 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5197 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5204 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5206 ar->clear_changes ();
5207 ar->set_fade_in_active (yn);
5208 _session->add_command (new StatefulDiffCommand (ar));
5211 commit_reversible_command ();
5215 Editor::set_fade_out_active (bool yn)
5217 RegionSelection rs = get_regions_from_selection_and_entered ();
5223 begin_reversible_command (_("set fade out active"));
5225 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5226 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5232 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5234 ar->clear_changes ();
5235 ar->set_fade_out_active (yn);
5236 _session->add_command(new StatefulDiffCommand (ar));
5239 commit_reversible_command ();
5243 Editor::toggle_region_fades (int dir)
5245 if (_ignore_region_action) {
5249 boost::shared_ptr<AudioRegion> ar;
5252 RegionSelection rs = get_regions_from_selection_and_entered ();
5258 RegionSelection::iterator i;
5259 for (i = rs.begin(); i != rs.end(); ++i) {
5260 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5262 yn = ar->fade_out_active ();
5264 yn = ar->fade_in_active ();
5270 if (i == rs.end()) {
5274 /* XXX should this undo-able? */
5276 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5277 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5280 if (dir == 1 || dir == 0) {
5281 ar->set_fade_in_active (!yn);
5284 if (dir == -1 || dir == 0) {
5285 ar->set_fade_out_active (!yn);
5291 /** Update region fade visibility after its configuration has been changed */
5293 Editor::update_region_fade_visibility ()
5295 bool _fade_visibility = _session->config.get_show_region_fades ();
5297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5298 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5300 if (_fade_visibility) {
5301 v->audio_view()->show_all_fades ();
5303 v->audio_view()->hide_all_fades ();
5310 Editor::set_edit_point ()
5315 if (!mouse_frame (where, ignored)) {
5321 if (selection->markers.empty()) {
5323 mouse_add_new_marker (where);
5328 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5331 loc->move_to (where);
5337 Editor::set_playhead_cursor ()
5339 if (entered_marker) {
5340 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5345 if (!mouse_frame (where, ignored)) {
5352 _session->request_locate (where, _session->transport_rolling());
5358 Editor::split_region ()
5360 if (((mouse_mode == MouseRange) ||
5361 (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5362 !selection->time.empty()) {
5363 separate_regions_between (selection->time);
5367 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5369 framepos_t where = get_preferred_edit_position ();
5375 split_regions_at (where, rs);
5379 Editor::ensure_entered_track_selected (bool op_really_wants_one_track_if_none_are_selected)
5381 if (entered_track && mouse_mode == MouseObject) {
5382 if (!selection->tracks.empty()) {
5383 if (!selection->selected (entered_track)) {
5384 selection->add (entered_track);
5387 /* there is no selection, but this operation requires/prefers selected objects */
5389 if (op_really_wants_one_track_if_none_are_selected) {
5390 selection->set (entered_track);
5396 struct EditorOrderRouteSorter {
5397 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5398 /* use of ">" forces the correct sort order */
5399 return a->order_key ("editor") < b->order_key ("editor");
5404 Editor::select_next_route()
5406 if (selection->tracks.empty()) {
5407 selection->set (track_views.front());
5411 TimeAxisView* current = selection->tracks.front();
5415 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5416 if (*i == current) {
5418 if (i != track_views.end()) {
5421 current = (*(track_views.begin()));
5422 //selection->set (*(track_views.begin()));
5427 rui = dynamic_cast<RouteUI *>(current);
5428 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5430 selection->set(current);
5432 ensure_track_visible(current);
5436 Editor::select_prev_route()
5438 if (selection->tracks.empty()) {
5439 selection->set (track_views.front());
5443 TimeAxisView* current = selection->tracks.front();
5447 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5448 if (*i == current) {
5450 if (i != track_views.rend()) {
5453 current = *(track_views.rbegin());
5458 rui = dynamic_cast<RouteUI *>(current);
5459 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5461 selection->set (current);
5463 ensure_track_visible(current);
5467 Editor::ensure_track_visible(TimeAxisView *track)
5469 if (track->hidden())
5472 double const current_view_min_y = vertical_adjustment.get_value();
5473 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5475 double const track_min_y = track->y_position ();
5476 double const track_max_y = track->y_position () + track->effective_height ();
5478 if (track_min_y >= current_view_min_y &&
5479 track_max_y <= current_view_max_y) {
5485 if (track_min_y < current_view_min_y) {
5486 // Track is above the current view
5487 new_value = track_min_y;
5489 // Track is below the current view
5490 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5493 vertical_adjustment.set_value(new_value);
5497 Editor::set_loop_from_selection (bool play)
5499 if (_session == 0 || selection->time.empty()) {
5503 framepos_t start = selection->time[clicked_selection].start;
5504 framepos_t end = selection->time[clicked_selection].end;
5506 set_loop_range (start, end, _("set loop range from selection"));
5509 _session->request_play_loop (true);
5510 _session->request_locate (start, true);
5515 Editor::set_loop_from_edit_range (bool play)
5517 if (_session == 0) {
5524 if (!get_edit_op_range (start, end)) {
5528 set_loop_range (start, end, _("set loop range from edit range"));
5531 _session->request_play_loop (true);
5532 _session->request_locate (start, true);
5537 Editor::set_loop_from_region (bool play)
5539 framepos_t start = max_framepos;
5542 RegionSelection rs = get_regions_from_selection_and_entered ();
5548 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5549 if ((*i)->region()->position() < start) {
5550 start = (*i)->region()->position();
5552 if ((*i)->region()->last_frame() + 1 > end) {
5553 end = (*i)->region()->last_frame() + 1;
5557 set_loop_range (start, end, _("set loop range from region"));
5560 _session->request_play_loop (true);
5561 _session->request_locate (start, true);
5566 Editor::set_punch_from_selection ()
5568 if (_session == 0 || selection->time.empty()) {
5572 framepos_t start = selection->time[clicked_selection].start;
5573 framepos_t end = selection->time[clicked_selection].end;
5575 set_punch_range (start, end, _("set punch range from selection"));
5579 Editor::set_punch_from_edit_range ()
5581 if (_session == 0) {
5588 if (!get_edit_op_range (start, end)) {
5592 set_punch_range (start, end, _("set punch range from edit range"));
5596 Editor::set_punch_from_region ()
5598 framepos_t start = max_framepos;
5601 RegionSelection rs = get_regions_from_selection_and_entered ();
5607 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5608 if ((*i)->region()->position() < start) {
5609 start = (*i)->region()->position();
5611 if ((*i)->region()->last_frame() + 1 > end) {
5612 end = (*i)->region()->last_frame() + 1;
5616 set_punch_range (start, end, _("set punch range from region"));
5620 Editor::pitch_shift_region ()
5622 RegionSelection rs = get_regions_from_selection_and_entered ();
5624 RegionSelection audio_rs;
5625 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5626 if (dynamic_cast<AudioRegionView*> (*i)) {
5627 audio_rs.push_back (*i);
5631 if (audio_rs.empty()) {
5635 pitch_shift (audio_rs, 1.2);
5639 Editor::transpose_region ()
5641 RegionSelection rs = get_regions_from_selection_and_entered ();
5643 list<MidiRegionView*> midi_region_views;
5644 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5645 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5647 midi_region_views.push_back (mrv);
5652 int const r = d.run ();
5653 if (r != RESPONSE_ACCEPT) {
5657 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5658 (*i)->midi_region()->transpose (d.semitones ());
5663 Editor::set_tempo_from_region ()
5665 RegionSelection rs = get_regions_from_selection_and_entered ();
5667 if (!_session || rs.empty()) {
5671 RegionView* rv = rs.front();
5673 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5677 Editor::use_range_as_bar ()
5679 framepos_t start, end;
5680 if (get_edit_op_range (start, end)) {
5681 define_one_bar (start, end);
5686 Editor::define_one_bar (framepos_t start, framepos_t end)
5688 framepos_t length = end - start;
5690 const Meter& m (_session->tempo_map().meter_at (start));
5692 /* length = 1 bar */
5694 /* now we want frames per beat.
5695 we have frames per bar, and beats per bar, so ...
5698 /* XXXX METER MATH */
5700 double frames_per_beat = length / m.divisions_per_bar();
5702 /* beats per minute = */
5704 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5706 /* now decide whether to:
5708 (a) set global tempo
5709 (b) add a new tempo marker
5713 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5715 bool do_global = false;
5717 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5719 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5720 at the start, or create a new marker
5723 vector<string> options;
5724 options.push_back (_("Cancel"));
5725 options.push_back (_("Add new marker"));
5726 options.push_back (_("Set global tempo"));
5729 _("Define one bar"),
5730 _("Do you want to set the global tempo or add a new tempo marker?"),
5734 c.set_default_response (2);
5750 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5751 if the marker is at the region starter, change it, otherwise add
5756 begin_reversible_command (_("set tempo from region"));
5757 XMLNode& before (_session->tempo_map().get_state());
5760 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5761 } else if (t.frame() == start) {
5762 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5764 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5767 XMLNode& after (_session->tempo_map().get_state());
5769 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5770 commit_reversible_command ();
5774 Editor::split_region_at_transients ()
5776 AnalysisFeatureList positions;
5778 RegionSelection rs = get_regions_from_selection_and_entered ();
5780 if (!_session || rs.empty()) {
5784 _session->begin_reversible_command (_("split regions"));
5786 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5788 RegionSelection::iterator tmp;
5793 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5795 if (ar && (ar->get_transients (positions) == 0)) {
5796 split_region_at_points ((*i)->region(), positions, true);
5803 _session->commit_reversible_command ();
5808 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5810 bool use_rhythmic_rodent = false;
5812 boost::shared_ptr<Playlist> pl = r->playlist();
5814 list<boost::shared_ptr<Region> > new_regions;
5820 if (positions.empty()) {
5825 if (positions.size() > 20 && can_ferret) {
5826 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);
5827 MessageDialog msg (msgstr,
5830 Gtk::BUTTONS_OK_CANCEL);
5833 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5834 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5836 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5839 msg.set_title (_("Excessive split?"));
5842 int response = msg.run();
5848 case RESPONSE_APPLY:
5849 use_rhythmic_rodent = true;
5856 if (use_rhythmic_rodent) {
5857 show_rhythm_ferret ();
5861 AnalysisFeatureList::const_iterator x;
5863 pl->clear_changes ();
5864 pl->clear_owned_changes ();
5866 x = positions.begin();
5868 if (x == positions.end()) {
5873 pl->remove_region (r);
5877 while (x != positions.end()) {
5879 /* deal with positons that are out of scope of present region bounds */
5880 if (*x <= 0 || *x > r->length()) {
5885 /* file start = original start + how far we from the initial position ?
5888 framepos_t file_start = r->start() + pos;
5890 /* length = next position - current position
5893 framepos_t len = (*x) - pos;
5895 /* XXX we do we really want to allow even single-sample regions?
5896 shouldn't we have some kind of lower limit on region size?
5905 if (RegionFactory::region_name (new_name, r->name())) {
5909 /* do NOT announce new regions 1 by one, just wait till they are all done */
5913 plist.add (ARDOUR::Properties::start, file_start);
5914 plist.add (ARDOUR::Properties::length, len);
5915 plist.add (ARDOUR::Properties::name, new_name);
5916 plist.add (ARDOUR::Properties::layer, 0);
5918 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5919 /* because we set annouce to false, manually add the new region to the
5922 RegionFactory::map_add (nr);
5924 pl->add_region (nr, r->position() + pos);
5927 new_regions.push_front(nr);
5936 RegionFactory::region_name (new_name, r->name());
5938 /* Add the final region */
5941 plist.add (ARDOUR::Properties::start, r->start() + pos);
5942 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5943 plist.add (ARDOUR::Properties::name, new_name);
5944 plist.add (ARDOUR::Properties::layer, 0);
5946 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5947 /* because we set annouce to false, manually add the new region to the
5950 RegionFactory::map_add (nr);
5951 pl->add_region (nr, r->position() + pos);
5954 new_regions.push_front(nr);
5959 /* We might have removed regions, which alters other regions' layering_index,
5960 so we need to do a recursive diff here.
5962 vector<Command*> cmds;
5964 _session->add_commands (cmds);
5966 _session->add_command (new StatefulDiffCommand (pl));
5970 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5971 set_selected_regionview_from_region_list ((*i), Selection::Add);
5977 Editor::place_transient()
5983 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5989 framepos_t where = get_preferred_edit_position();
5991 _session->begin_reversible_command (_("place transient"));
5993 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5994 framepos_t position = (*r)->region()->position();
5995 (*r)->region()->add_transient(where - position);
5998 _session->commit_reversible_command ();
6002 Editor::remove_transient(ArdourCanvas::Item* item)
6008 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6011 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6012 _arv->remove_transient (*(float*) _line->get_data ("position"));
6016 Editor::snap_regions_to_grid ()
6018 list <boost::shared_ptr<Playlist > > used_playlists;
6020 RegionSelection rs = get_regions_from_selection_and_entered ();
6022 if (!_session || rs.empty()) {
6026 _session->begin_reversible_command (_("snap regions to grid"));
6028 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6030 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6032 if (!pl->frozen()) {
6033 /* we haven't seen this playlist before */
6035 /* remember used playlists so we can thaw them later */
6036 used_playlists.push_back(pl);
6040 framepos_t start_frame = (*r)->region()->first_frame ();
6041 snap_to (start_frame);
6042 (*r)->region()->set_position (start_frame);
6045 while (used_playlists.size() > 0) {
6046 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6048 used_playlists.pop_front();
6051 _session->commit_reversible_command ();
6055 Editor::close_region_gaps ()
6057 list <boost::shared_ptr<Playlist > > used_playlists;
6059 RegionSelection rs = get_regions_from_selection_and_entered ();
6061 if (!_session || rs.empty()) {
6065 Dialog dialog (_("Close Region Gaps"));
6068 table.set_spacings (12);
6069 table.set_border_width (12);
6070 Label* l = manage (new Label (_("Crossfade length")));
6071 l->set_alignment (0, 0.5);
6072 table.attach (*l, 0, 1, 0, 1);
6074 SpinButton spin_crossfade (1, 0);
6075 spin_crossfade.set_range (0, 15);
6076 spin_crossfade.set_increments (1, 1);
6077 spin_crossfade.set_value (5);
6078 table.attach (spin_crossfade, 1, 2, 0, 1);
6080 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6082 l = manage (new Label (_("Pull-back length")));
6083 l->set_alignment (0, 0.5);
6084 table.attach (*l, 0, 1, 1, 2);
6086 SpinButton spin_pullback (1, 0);
6087 spin_pullback.set_range (0, 100);
6088 spin_pullback.set_increments (1, 1);
6089 spin_pullback.set_value(30);
6090 table.attach (spin_pullback, 1, 2, 1, 2);
6092 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6094 dialog.get_vbox()->pack_start (table);
6095 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6096 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6099 if (dialog.run () == RESPONSE_CANCEL) {
6103 framepos_t crossfade_len = spin_crossfade.get_value();
6104 framepos_t pull_back_frames = spin_pullback.get_value();
6106 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6107 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6109 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6111 _session->begin_reversible_command (_("close region gaps"));
6114 boost::shared_ptr<Region> last_region;
6116 rs.sort_by_position_and_track();
6118 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6120 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6122 if (!pl->frozen()) {
6123 /* we haven't seen this playlist before */
6125 /* remember used playlists so we can thaw them later */
6126 used_playlists.push_back(pl);
6130 framepos_t position = (*r)->region()->position();
6132 if (idx == 0 || position < last_region->position()){
6133 last_region = (*r)->region();
6138 (*r)->region()->trim_front( (position - pull_back_frames));
6139 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6141 last_region = (*r)->region();
6146 while (used_playlists.size() > 0) {
6147 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6149 used_playlists.pop_front();
6152 _session->commit_reversible_command ();
6156 Editor::tab_to_transient (bool forward)
6158 AnalysisFeatureList positions;
6160 RegionSelection rs = get_regions_from_selection_and_entered ();
6166 framepos_t pos = _session->audible_frame ();
6168 if (!selection->tracks.empty()) {
6170 /* don't waste time searching for transients in duplicate playlists.
6173 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6175 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6177 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6180 boost::shared_ptr<Track> tr = rtv->track();
6182 boost::shared_ptr<Playlist> pl = tr->playlist ();
6184 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6187 positions.push_back (result);
6200 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6201 (*r)->region()->get_transients (positions);
6205 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6208 AnalysisFeatureList::iterator x;
6210 for (x = positions.begin(); x != positions.end(); ++x) {
6216 if (x != positions.end ()) {
6217 _session->request_locate (*x);
6221 AnalysisFeatureList::reverse_iterator x;
6223 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6229 if (x != positions.rend ()) {
6230 _session->request_locate (*x);
6236 Editor::playhead_forward_to_grid ()
6238 if (!_session) return;
6239 framepos_t pos = playhead_cursor->current_frame;
6240 if (pos < max_framepos - 1) {
6242 snap_to_internal (pos, 1, false);
6243 _session->request_locate (pos);
6249 Editor::playhead_backward_to_grid ()
6251 if (!_session) return;
6252 framepos_t pos = playhead_cursor->current_frame;
6255 snap_to_internal (pos, -1, false);
6256 _session->request_locate (pos);
6261 Editor::set_track_height (Height h)
6263 TrackSelection& ts (selection->tracks);
6265 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6266 (*x)->set_height_enum (h);
6271 Editor::toggle_tracks_active ()
6273 TrackSelection& ts (selection->tracks);
6275 bool target = false;
6281 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6282 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6286 target = !rtv->_route->active();
6289 rtv->_route->set_active (target, this);
6295 Editor::remove_tracks ()
6297 TrackSelection& ts (selection->tracks);
6303 vector<string> choices;
6307 const char* trackstr;
6309 vector<boost::shared_ptr<Route> > routes;
6310 bool special_bus = false;
6312 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6313 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6315 if (rtv->is_track()) {
6321 routes.push_back (rtv->_route);
6323 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6328 if (special_bus && !Config->get_allow_special_bus_removal()) {
6329 MessageDialog msg (_("That would be bad news ...."),
6333 msg.set_secondary_text (string_compose (_(
6334 "Removing the master or monitor bus is such a bad idea\n\
6335 that %1 is not going to allow it.\n\
6337 If you really want to do this sort of thing\n\
6338 edit your ardour.rc file to set the\n\
6339 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6346 if (ntracks + nbusses == 0) {
6351 trackstr = _("tracks");
6353 trackstr = _("track");
6357 busstr = _("busses");
6364 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6365 "(You may also lose the playlists associated with the %2)\n\n"
6366 "This action cannot be undone, and the session file will be overwritten!"),
6367 ntracks, trackstr, nbusses, busstr);
6369 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6370 "(You may also lose the playlists associated with the %2)\n\n"
6371 "This action cannot be undone, and the session file will be overwritten!"),
6374 } else if (nbusses) {
6375 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6376 "This action cannot be undon, and the session file will be overwritten"),
6380 choices.push_back (_("No, do nothing."));
6381 if (ntracks + nbusses > 1) {
6382 choices.push_back (_("Yes, remove them."));
6384 choices.push_back (_("Yes, remove it."));
6389 title = string_compose (_("Remove %1"), trackstr);
6391 title = string_compose (_("Remove %1"), busstr);
6394 Choice prompter (title, prompt, choices);
6396 if (prompter.run () != 1) {
6400 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6401 _session->remove_route (*x);
6406 Editor::do_insert_time ()
6408 if (selection->tracks.empty()) {
6412 InsertTimeDialog d (*this);
6413 int response = d.run ();
6415 if (response != RESPONSE_OK) {
6419 if (d.distance() == 0) {
6423 InsertTimeOption opt = d.intersected_region_action ();
6426 get_preferred_edit_position(),
6432 d.move_glued_markers(),
6433 d.move_locked_markers(),
6439 Editor::insert_time (
6440 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6441 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6444 bool commit = false;
6446 if (Config->get_edit_mode() == Lock) {
6450 begin_reversible_command (_("insert time"));
6452 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6454 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6458 /* don't operate on any playlist more than once, which could
6459 * happen if "all playlists" is enabled, but there is more
6460 * than 1 track using playlists "from" a given track.
6463 set<boost::shared_ptr<Playlist> > pl;
6465 if (all_playlists) {
6466 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6468 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6469 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6474 if ((*x)->playlist ()) {
6475 pl.insert ((*x)->playlist ());
6479 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6481 (*i)->clear_changes ();
6482 (*i)->clear_owned_changes ();
6484 if (opt == SplitIntersected) {
6488 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6490 vector<Command*> cmds;
6492 _session->add_commands (cmds);
6494 _session->add_command (new StatefulDiffCommand (*i));
6499 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6501 rtav->route ()->shift (pos, frames);
6509 XMLNode& before (_session->locations()->get_state());
6510 Locations::LocationList copy (_session->locations()->list());
6512 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6514 Locations::LocationList::const_iterator tmp;
6516 bool const was_locked = (*i)->locked ();
6517 if (locked_markers_too) {
6521 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6523 if ((*i)->start() >= pos) {
6524 (*i)->set_start ((*i)->start() + frames);
6525 if (!(*i)->is_mark()) {
6526 (*i)->set_end ((*i)->end() + frames);
6539 XMLNode& after (_session->locations()->get_state());
6540 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6545 _session->tempo_map().insert_time (pos, frames);
6549 commit_reversible_command ();
6554 Editor::fit_selected_tracks ()
6556 if (!selection->tracks.empty()) {
6557 fit_tracks (selection->tracks);
6561 /* no selected tracks - use tracks with selected regions */
6563 if (!selection->regions.empty()) {
6564 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6565 tvl.push_back (&(*r)->get_time_axis_view ());
6571 } else if (internal_editing()) {
6572 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6575 if (entered_track) {
6576 tvl.push_back (entered_track);
6584 Editor::fit_tracks (TrackViewList & tracks)
6586 if (tracks.empty()) {
6590 uint32_t child_heights = 0;
6591 int visible_tracks = 0;
6593 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6595 if (!(*t)->marked_for_display()) {
6599 child_heights += (*t)->effective_height() - (*t)->current_height();
6603 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6604 double first_y_pos = DBL_MAX;
6606 if (h < TimeAxisView::preset_height (HeightSmall)) {
6607 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6608 /* too small to be displayed */
6612 undo_visual_stack.push_back (current_visual_state (true));
6613 no_save_visual = true;
6615 /* build a list of all tracks, including children */
6618 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6620 TimeAxisView::Children c = (*i)->get_child_list ();
6621 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6622 all.push_back (j->get());
6626 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6628 bool prev_was_selected = false;
6629 bool is_selected = tracks.contains (all.front());
6630 bool next_is_selected;
6632 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6634 TrackViewList::iterator next;
6639 if (next != all.end()) {
6640 next_is_selected = tracks.contains (*next);
6642 next_is_selected = false;
6645 if ((*t)->marked_for_display ()) {
6647 (*t)->set_height (h);
6648 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6650 if (prev_was_selected && next_is_selected) {
6651 hide_track_in_display (*t);
6656 prev_was_selected = is_selected;
6657 is_selected = next_is_selected;
6661 set the controls_layout height now, because waiting for its size
6662 request signal handler will cause the vertical adjustment setting to fail
6665 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6666 vertical_adjustment.set_value (first_y_pos);
6668 redo_visual_stack.push_back (current_visual_state (true));
6672 Editor::save_visual_state (uint32_t n)
6674 while (visual_states.size() <= n) {
6675 visual_states.push_back (0);
6678 if (visual_states[n] != 0) {
6679 delete visual_states[n];
6682 visual_states[n] = current_visual_state (true);
6687 Editor::goto_visual_state (uint32_t n)
6689 if (visual_states.size() <= n) {
6693 if (visual_states[n] == 0) {
6697 use_visual_state (*visual_states[n]);
6701 Editor::start_visual_state_op (uint32_t n)
6703 save_visual_state (n);
6705 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6707 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6708 pup->set_text (buf);
6713 Editor::cancel_visual_state_op (uint32_t n)
6715 goto_visual_state (n);
6719 Editor::toggle_region_mute ()
6721 if (_ignore_region_action) {
6725 RegionSelection rs = get_regions_from_selection_and_entered ();
6731 if (rs.size() > 1) {
6732 begin_reversible_command (_("mute regions"));
6734 begin_reversible_command (_("mute region"));
6737 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6739 (*i)->region()->playlist()->clear_changes ();
6740 (*i)->region()->set_muted (!(*i)->region()->muted ());
6741 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6745 commit_reversible_command ();
6749 Editor::combine_regions ()
6751 /* foreach track with selected regions, take all selected regions
6752 and join them into a new region containing the subregions (as a
6756 typedef set<RouteTimeAxisView*> RTVS;
6759 if (selection->regions.empty()) {
6763 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6764 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6767 tracks.insert (rtv);
6771 begin_reversible_command (_("combine regions"));
6773 vector<RegionView*> new_selection;
6775 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6778 if ((rv = (*i)->combine_regions ()) != 0) {
6779 new_selection.push_back (rv);
6783 selection->clear_regions ();
6784 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6785 selection->add (*i);
6788 commit_reversible_command ();
6792 Editor::uncombine_regions ()
6794 typedef set<RouteTimeAxisView*> RTVS;
6797 if (selection->regions.empty()) {
6801 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6802 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6805 tracks.insert (rtv);
6809 begin_reversible_command (_("uncombine regions"));
6811 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6812 (*i)->uncombine_regions ();
6815 commit_reversible_command ();