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;
1340 nfpu = min (9e6, nfpu * 1.61803399);
1342 nfpu = max (1.0, nfpu / 1.61803399);
1345 temporal_zoom (nfpu);
1349 Editor::temporal_zoom (gdouble fpu)
1351 if (!_session) return;
1353 framepos_t current_page = current_page_frames();
1354 framepos_t current_leftmost = leftmost_frame;
1355 framepos_t current_rightmost;
1356 framepos_t current_center;
1357 framepos_t new_page_size;
1358 framepos_t half_page_size;
1359 framepos_t leftmost_after_zoom = 0;
1361 bool in_track_canvas;
1365 /* XXX this limit is also in ::set_frames_per_unit() */
1367 if (frames_per_unit <= 1.0 && fpu <= frames_per_unit) {
1373 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1374 // segfaults for lack of memory. If somebody decides this is not high enough I
1375 // believe it can be raisen to higher values but some limit must be in place.
1380 new_page_size = (framepos_t) floor (_canvas_width * nfpu);
1381 half_page_size = new_page_size / 2;
1383 switch (zoom_focus) {
1385 leftmost_after_zoom = current_leftmost;
1388 case ZoomFocusRight:
1389 current_rightmost = leftmost_frame + current_page;
1390 if (current_rightmost < new_page_size) {
1391 leftmost_after_zoom = 0;
1393 leftmost_after_zoom = current_rightmost - new_page_size;
1397 case ZoomFocusCenter:
1398 current_center = current_leftmost + (current_page/2);
1399 if (current_center < half_page_size) {
1400 leftmost_after_zoom = 0;
1402 leftmost_after_zoom = current_center - half_page_size;
1406 case ZoomFocusPlayhead:
1407 /* centre playhead */
1408 l = playhead_cursor->current_frame - (new_page_size * 0.5);
1411 leftmost_after_zoom = 0;
1412 } else if (l > max_framepos) {
1413 leftmost_after_zoom = max_framepos - new_page_size;
1415 leftmost_after_zoom = (framepos_t) l;
1419 case ZoomFocusMouse:
1420 /* try to keep the mouse over the same point in the display */
1422 if (!mouse_frame (where, in_track_canvas)) {
1423 /* use playhead instead */
1424 where = playhead_cursor->current_frame;
1426 if (where < half_page_size) {
1427 leftmost_after_zoom = 0;
1429 leftmost_after_zoom = where - half_page_size;
1434 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1437 leftmost_after_zoom = 0;
1438 } else if (l > max_framepos) {
1439 leftmost_after_zoom = max_framepos - new_page_size;
1441 leftmost_after_zoom = (framepos_t) l;
1448 /* try to keep the edit point in the same place */
1449 where = get_preferred_edit_position ();
1453 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1456 leftmost_after_zoom = 0;
1457 } else if (l > max_framepos) {
1458 leftmost_after_zoom = max_framepos - new_page_size;
1460 leftmost_after_zoom = (framepos_t) l;
1464 /* edit point not defined */
1471 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1473 reposition_and_zoom (leftmost_after_zoom, nfpu);
1477 Editor::temporal_zoom_region (bool both_axes)
1479 framepos_t start = max_framepos;
1481 set<TimeAxisView*> tracks;
1483 RegionSelection rs = get_regions_from_selection_and_entered ();
1489 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1491 if ((*i)->region()->position() < start) {
1492 start = (*i)->region()->position();
1495 if ((*i)->region()->last_frame() + 1 > end) {
1496 end = (*i)->region()->last_frame() + 1;
1499 tracks.insert (&((*i)->get_time_axis_view()));
1502 /* now comes an "interesting" hack ... make sure we leave a little space
1503 at each end of the editor so that the zoom doesn't fit the region
1504 precisely to the screen.
1507 GdkScreen* screen = gdk_screen_get_default ();
1508 gint pixwidth = gdk_screen_get_width (screen);
1509 gint mmwidth = gdk_screen_get_width_mm (screen);
1510 double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1511 double one_centimeter_in_pixels = pix_per_mm * 10.0;
1513 if ((start == 0 && end == 0) || end < start) {
1517 framepos_t range = end - start;
1518 double new_fpu = (double)range / (double)_canvas_width;
1519 framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpu);
1521 if (start > extra_samples) {
1522 start -= extra_samples;
1527 if (max_framepos - extra_samples > end) {
1528 end += extra_samples;
1533 /* if we're zooming on both axes we need to save track heights etc.
1536 undo_visual_stack.push_back (current_visual_state (both_axes));
1538 PBD::Unwinder<bool> nsv (no_save_visual, true);
1540 temporal_zoom_by_frame (start, end);
1543 uint32_t per_track_height = (uint32_t) floor ((_canvas_height - canvas_timebars_vsize - 10.0) / tracks.size());
1545 /* set visible track heights appropriately */
1547 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1548 (*t)->set_height (per_track_height);
1551 /* hide irrelevant tracks */
1553 _routes->suspend_redisplay ();
1555 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1556 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1557 hide_track_in_display (*i);
1561 _routes->resume_redisplay ();
1563 vertical_adjustment.set_value (0.0);
1566 redo_visual_stack.push_back (current_visual_state (both_axes));
1570 Editor::zoom_to_region (bool both_axes)
1572 temporal_zoom_region (both_axes);
1576 Editor::temporal_zoom_selection ()
1578 if (!selection) return;
1580 if (selection->time.empty()) {
1584 framepos_t start = selection->time[clicked_selection].start;
1585 framepos_t end = selection->time[clicked_selection].end;
1587 temporal_zoom_by_frame (start, end);
1591 Editor::temporal_zoom_session ()
1593 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1596 framecnt_t const l = _session->current_end_frame() - _session->current_start_frame();
1597 double s = _session->current_start_frame() - l * 0.01;
1601 framecnt_t const e = _session->current_end_frame() + l * 0.01;
1602 temporal_zoom_by_frame (framecnt_t (s), e);
1607 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
1609 if (!_session) return;
1611 if ((start == 0 && end == 0) || end < start) {
1615 framepos_t range = end - start;
1617 double new_fpu = (double)range / (double)_canvas_width;
1619 framepos_t new_page = (framepos_t) floor (_canvas_width * new_fpu);
1620 framepos_t middle = (framepos_t) floor( (double)start + ((double)range / 2.0f ));
1621 framepos_t new_leftmost = (framepos_t) floor( (double)middle - ((double)new_page/2.0f));
1623 if (new_leftmost > middle) {
1627 if (new_leftmost < 0) {
1631 reposition_and_zoom (new_leftmost, new_fpu);
1635 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
1640 double range_before = frame - leftmost_frame;
1643 new_fpu = frames_per_unit;
1646 new_fpu *= 1.61803399;
1647 range_before *= 1.61803399;
1649 new_fpu = max(1.0,(new_fpu/1.61803399));
1650 range_before /= 1.61803399;
1653 if (new_fpu == frames_per_unit) {
1657 framepos_t new_leftmost = frame - (framepos_t)range_before;
1659 if (new_leftmost > frame) {
1663 if (new_leftmost < 0) {
1667 reposition_and_zoom (new_leftmost, new_fpu);
1672 Editor::choose_new_marker_name(string &name) {
1674 if (!Config->get_name_new_markers()) {
1675 /* don't prompt user for a new name */
1679 ArdourPrompter dialog (true);
1681 dialog.set_prompt (_("New Name:"));
1683 dialog.set_title (_("New Location Marker"));
1685 dialog.set_name ("MarkNameWindow");
1686 dialog.set_size_request (250, -1);
1687 dialog.set_position (Gtk::WIN_POS_MOUSE);
1689 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
1690 dialog.set_initial_text (name);
1694 switch (dialog.run ()) {
1695 case RESPONSE_ACCEPT:
1701 dialog.get_result(name);
1708 Editor::add_location_from_selection ()
1712 if (selection->time.empty()) {
1716 if (_session == 0 || clicked_axisview == 0) {
1720 framepos_t start = selection->time[clicked_selection].start;
1721 framepos_t end = selection->time[clicked_selection].end;
1723 _session->locations()->next_available_name(rangename,"selection");
1724 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
1726 _session->begin_reversible_command (_("add marker"));
1727 XMLNode &before = _session->locations()->get_state();
1728 _session->locations()->add (location, true);
1729 XMLNode &after = _session->locations()->get_state();
1730 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1731 _session->commit_reversible_command ();
1735 Editor::add_location_mark (framepos_t where)
1739 select_new_marker = true;
1741 _session->locations()->next_available_name(markername,"mark");
1742 if (!choose_new_marker_name(markername)) {
1745 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
1746 _session->begin_reversible_command (_("add marker"));
1747 XMLNode &before = _session->locations()->get_state();
1748 _session->locations()->add (location, true);
1749 XMLNode &after = _session->locations()->get_state();
1750 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1751 _session->commit_reversible_command ();
1755 Editor::add_location_from_playhead_cursor ()
1757 add_location_mark (_session->audible_frame());
1760 /** Add a range marker around each selected region */
1762 Editor::add_locations_from_region ()
1764 RegionSelection rs = get_regions_from_selection_and_entered ();
1770 _session->begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
1771 XMLNode &before = _session->locations()->get_state();
1773 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
1775 boost::shared_ptr<Region> region = (*i)->region ();
1777 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
1779 _session->locations()->add (location, true);
1782 XMLNode &after = _session->locations()->get_state();
1783 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1784 _session->commit_reversible_command ();
1787 /** Add a single range marker around all selected regions */
1789 Editor::add_location_from_region ()
1791 RegionSelection rs = get_regions_from_selection_and_entered ();
1797 _session->begin_reversible_command (_("add marker"));
1798 XMLNode &before = _session->locations()->get_state();
1802 if (rs.size() > 1) {
1803 _session->locations()->next_available_name(markername, "regions");
1805 RegionView* rv = *(rs.begin());
1806 boost::shared_ptr<Region> region = rv->region();
1807 markername = region->name();
1810 if (!choose_new_marker_name(markername)) {
1814 // single range spanning all selected
1815 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
1816 _session->locations()->add (location, true);
1818 XMLNode &after = _session->locations()->get_state();
1819 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1820 _session->commit_reversible_command ();
1826 Editor::jump_forward_to_mark ()
1832 Location *location = _session->locations()->first_location_after (playhead_cursor->current_frame);
1835 _session->request_locate (location->start(), _session->transport_rolling());
1837 _session->request_locate (_session->current_end_frame());
1842 Editor::jump_backward_to_mark ()
1848 Location *location = _session->locations()->first_location_before (playhead_cursor->current_frame);
1851 _session->request_locate (location->start(), _session->transport_rolling());
1853 _session->goto_start ();
1860 framepos_t const pos = _session->audible_frame ();
1863 _session->locations()->next_available_name (markername, "mark");
1865 if (!choose_new_marker_name (markername)) {
1869 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
1873 Editor::clear_markers ()
1876 _session->begin_reversible_command (_("clear markers"));
1877 XMLNode &before = _session->locations()->get_state();
1878 _session->locations()->clear_markers ();
1879 XMLNode &after = _session->locations()->get_state();
1880 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1881 _session->commit_reversible_command ();
1886 Editor::clear_ranges ()
1889 _session->begin_reversible_command (_("clear ranges"));
1890 XMLNode &before = _session->locations()->get_state();
1892 Location * looploc = _session->locations()->auto_loop_location();
1893 Location * punchloc = _session->locations()->auto_punch_location();
1894 Location * sessionloc = _session->locations()->session_range_location();
1896 _session->locations()->clear_ranges ();
1898 if (looploc) _session->locations()->add (looploc);
1899 if (punchloc) _session->locations()->add (punchloc);
1900 if (sessionloc) _session->locations()->add (sessionloc);
1902 XMLNode &after = _session->locations()->get_state();
1903 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1904 _session->commit_reversible_command ();
1909 Editor::clear_locations ()
1911 _session->begin_reversible_command (_("clear locations"));
1912 XMLNode &before = _session->locations()->get_state();
1913 _session->locations()->clear ();
1914 XMLNode &after = _session->locations()->get_state();
1915 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
1916 _session->commit_reversible_command ();
1917 _session->locations()->clear ();
1921 Editor::unhide_markers ()
1923 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1924 Location *l = (*i).first;
1925 if (l->is_hidden() && l->is_mark()) {
1926 l->set_hidden(false, this);
1932 Editor::unhide_ranges ()
1934 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
1935 Location *l = (*i).first;
1936 if (l->is_hidden() && l->is_range_marker()) {
1937 l->set_hidden(false, this);
1942 /* INSERT/REPLACE */
1945 Editor::insert_region_list_drag (boost::shared_ptr<Region> region, int x, int y)
1950 RouteTimeAxisView *rtv = 0;
1951 boost::shared_ptr<Playlist> playlist;
1953 track_canvas->window_to_world (x, y, wx, wy);
1956 event.type = GDK_BUTTON_RELEASE;
1957 event.button.x = wx;
1958 event.button.y = wy;
1960 where = event_frame (&event, &cx, &cy);
1962 if (where < leftmost_frame || where > leftmost_frame + current_page_frames()) {
1963 /* clearly outside canvas area */
1967 std::pair<TimeAxisView*, int> tv = trackview_by_y_position (cy);
1968 if (tv.first == 0) {
1972 if ((rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
1976 if ((playlist = rtv->playlist()) == 0) {
1982 begin_reversible_command (_("insert dragged region"));
1983 playlist->clear_changes ();
1984 playlist->add_region (RegionFactory::create (region, true), where, 1.0);
1985 _session->add_command(new StatefulDiffCommand (playlist));
1986 commit_reversible_command ();
1990 Editor::insert_route_list_drag (boost::shared_ptr<Route> route, int x, int y)
1994 RouteTimeAxisView *dest_rtv = 0;
1995 RouteTimeAxisView *source_rtv = 0;
1997 track_canvas->window_to_world (x, y, wx, wy);
1998 wx += horizontal_position ();
1999 wy += vertical_adjustment.get_value();
2002 event.type = GDK_BUTTON_RELEASE;
2003 event.button.x = wx;
2004 event.button.y = wy;
2006 event_frame (&event, &cx, &cy);
2008 std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (cy);
2009 if (tv.first == 0) {
2013 if ((dest_rtv = dynamic_cast<RouteTimeAxisView*> (tv.first)) == 0) {
2017 /* use this drag source to add underlay to a track. But we really don't care
2018 about the Route, only the view of the route, so find it first */
2019 for(TrackViewList::iterator it = track_views.begin(); it != track_views.end(); ++it) {
2020 if((source_rtv = dynamic_cast<RouteTimeAxisView*>(*it)) == 0) {
2024 if(source_rtv->route() == route && source_rtv != dest_rtv) {
2025 dest_rtv->add_underlay(source_rtv->view());
2032 Editor::insert_region_list_selection (float times)
2034 RouteTimeAxisView *tv = 0;
2035 boost::shared_ptr<Playlist> playlist;
2037 if (clicked_routeview != 0) {
2038 tv = clicked_routeview;
2039 } else if (!selection->tracks.empty()) {
2040 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2043 } else if (entered_track != 0) {
2044 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2051 if ((playlist = tv->playlist()) == 0) {
2055 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2060 begin_reversible_command (_("insert region"));
2061 playlist->clear_changes ();
2062 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2063 _session->add_command(new StatefulDiffCommand (playlist));
2064 commit_reversible_command ();
2067 /* BUILT-IN EFFECTS */
2070 Editor::reverse_selection ()
2075 /* GAIN ENVELOPE EDITING */
2078 Editor::edit_envelope ()
2085 Editor::transition_to_rolling (bool fwd)
2091 if (_session->config.get_external_sync()) {
2092 switch (_session->config.get_sync_source()) {
2096 /* transport controlled by the master */
2101 if (_session->is_auditioning()) {
2102 _session->cancel_audition ();
2106 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2110 Editor::play_from_start ()
2112 _session->request_locate (_session->current_start_frame(), true);
2116 Editor::play_from_edit_point ()
2118 _session->request_locate (get_preferred_edit_position(), true);
2122 Editor::play_from_edit_point_and_return ()
2124 framepos_t start_frame;
2125 framepos_t return_frame;
2127 start_frame = get_preferred_edit_position (true);
2129 if (_session->transport_rolling()) {
2130 _session->request_locate (start_frame, false);
2134 /* don't reset the return frame if its already set */
2136 if ((return_frame = _session->requested_return_frame()) < 0) {
2137 return_frame = _session->audible_frame();
2140 if (start_frame >= 0) {
2141 _session->request_roll_at_and_return (start_frame, return_frame);
2146 Editor::play_selection ()
2148 if (selection->time.empty()) {
2152 _session->request_play_range (&selection->time, true);
2156 Editor::play_location (Location& location)
2158 if (location.start() <= location.end()) {
2162 _session->request_bounded_roll (location.start(), location.end());
2166 Editor::loop_location (Location& location)
2168 if (location.start() <= location.end()) {
2174 if ((tll = transport_loop_location()) != 0) {
2175 tll->set (location.start(), location.end());
2177 // enable looping, reposition and start rolling
2178 _session->request_play_loop (true);
2179 _session->request_locate (tll->start(), true);
2184 Editor::do_layer_operation (LayerOperation op)
2186 if (selection->regions.empty ()) {
2190 bool const multiple = selection->regions.size() > 1;
2194 begin_reversible_command (_("raise regions"));
2196 begin_reversible_command (_("raise region"));
2202 begin_reversible_command (_("raise regions to top"));
2204 begin_reversible_command (_("raise region to top"));
2210 begin_reversible_command (_("lower regions"));
2212 begin_reversible_command (_("lower region"));
2218 begin_reversible_command (_("lower regions to bottom"));
2220 begin_reversible_command (_("lower region"));
2225 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2226 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2227 (*i)->clear_owned_changes ();
2230 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2231 boost::shared_ptr<Region> r = (*i)->region ();
2243 r->lower_to_bottom ();
2247 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2248 vector<Command*> cmds;
2250 _session->add_commands (cmds);
2253 commit_reversible_command ();
2257 Editor::raise_region ()
2259 do_layer_operation (Raise);
2263 Editor::raise_region_to_top ()
2265 do_layer_operation (RaiseToTop);
2269 Editor::lower_region ()
2271 do_layer_operation (Lower);
2275 Editor::lower_region_to_bottom ()
2277 do_layer_operation (LowerToBottom);
2280 /** Show the region editor for the selected regions */
2282 Editor::show_region_properties ()
2284 selection->foreach_regionview (&RegionView::show_region_editor);
2287 /** Show the midi list editor for the selected MIDI regions */
2289 Editor::show_midi_list_editor ()
2291 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2295 Editor::rename_region ()
2297 RegionSelection rs = get_regions_from_selection_and_entered ();
2303 ArdourDialog d (*this, _("Rename Region"), true, false);
2305 Label label (_("New name:"));
2308 hbox.set_spacing (6);
2309 hbox.pack_start (label, false, false);
2310 hbox.pack_start (entry, true, true);
2312 d.get_vbox()->set_border_width (12);
2313 d.get_vbox()->pack_start (hbox, false, false);
2315 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2316 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2318 d.set_size_request (300, -1);
2319 d.set_position (Gtk::WIN_POS_MOUSE);
2321 entry.set_text (rs.front()->region()->name());
2322 entry.select_region (0, -1);
2324 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2330 int const ret = d.run();
2334 if (ret != RESPONSE_OK) {
2338 std::string str = entry.get_text();
2339 strip_whitespace_edges (str);
2341 rs.front()->region()->set_name (str);
2342 _regions->redisplay ();
2347 Editor::audition_playlist_region_via_route (boost::shared_ptr<Region> region, Route& route)
2349 if (_session->is_auditioning()) {
2350 _session->cancel_audition ();
2353 // note: some potential for creativity here, because region doesn't
2354 // have to belong to the playlist that Route is handling
2356 // bool was_soloed = route.soloed();
2358 route.set_solo (true, this);
2360 _session->request_bounded_roll (region->position(), region->position() + region->length());
2362 /* XXX how to unset the solo state ? */
2365 /** Start an audition of the first selected region */
2367 Editor::play_edit_range ()
2369 framepos_t start, end;
2371 if (get_edit_op_range (start, end)) {
2372 _session->request_bounded_roll (start, end);
2377 Editor::play_selected_region ()
2379 framepos_t start = max_framepos;
2382 RegionSelection rs = get_regions_from_selection_and_entered ();
2388 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2389 if ((*i)->region()->position() < start) {
2390 start = (*i)->region()->position();
2392 if ((*i)->region()->last_frame() + 1 > end) {
2393 end = (*i)->region()->last_frame() + 1;
2397 _session->request_bounded_roll (start, end);
2401 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2403 _session->audition_region (region);
2407 Editor::region_from_selection ()
2409 if (clicked_axisview == 0) {
2413 if (selection->time.empty()) {
2417 framepos_t start = selection->time[clicked_selection].start;
2418 framepos_t end = selection->time[clicked_selection].end;
2420 TrackViewList tracks = get_tracks_for_range_action ();
2422 framepos_t selection_cnt = end - start + 1;
2424 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2425 boost::shared_ptr<Region> current;
2426 boost::shared_ptr<Playlist> pl;
2427 framepos_t internal_start;
2430 if ((pl = (*i)->playlist()) == 0) {
2434 if ((current = pl->top_region_at (start)) == 0) {
2438 internal_start = start - current->position();
2439 RegionFactory::region_name (new_name, current->name(), true);
2443 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2444 plist.add (ARDOUR::Properties::length, selection_cnt);
2445 plist.add (ARDOUR::Properties::name, new_name);
2446 plist.add (ARDOUR::Properties::layer, 0);
2448 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2453 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2455 if (selection->time.empty() || selection->tracks.empty()) {
2459 framepos_t start = selection->time[clicked_selection].start;
2460 framepos_t end = selection->time[clicked_selection].end;
2462 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2463 sort_track_selection (ts);
2465 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2466 boost::shared_ptr<Region> current;
2467 boost::shared_ptr<Playlist> playlist;
2468 framepos_t internal_start;
2471 if ((playlist = (*i)->playlist()) == 0) {
2475 if ((current = playlist->top_region_at(start)) == 0) {
2479 internal_start = start - current->position();
2480 RegionFactory::region_name (new_name, current->name(), true);
2484 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2485 plist.add (ARDOUR::Properties::length, end - start + 1);
2486 plist.add (ARDOUR::Properties::name, new_name);
2488 new_regions.push_back (RegionFactory::create (current, plist));
2493 Editor::split_multichannel_region ()
2495 RegionSelection rs = get_regions_from_selection_and_entered ();
2501 vector< boost::shared_ptr<Region> > v;
2503 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2504 (*x)->region()->separate_by_channel (*_session, v);
2509 Editor::new_region_from_selection ()
2511 region_from_selection ();
2512 cancel_selection ();
2516 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
2518 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
2519 case Evoral::OverlapNone:
2527 * - selected tracks, or if there are none...
2528 * - tracks containing selected regions, or if there are none...
2533 Editor::get_tracks_for_range_action () const
2537 if (selection->tracks.empty()) {
2539 /* use tracks with selected regions */
2541 RegionSelection rs = selection->regions;
2543 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2544 TimeAxisView* tv = &(*i)->get_time_axis_view();
2546 if (!t.contains (tv)) {
2552 /* no regions and no tracks: use all tracks */
2558 t = selection->tracks;
2561 return t.filter_to_unique_playlists();
2565 Editor::separate_regions_between (const TimeSelection& ts)
2567 bool in_command = false;
2568 boost::shared_ptr<Playlist> playlist;
2569 RegionSelection new_selection;
2571 TrackViewList tmptracks = get_tracks_for_range_action ();
2572 sort_track_selection (tmptracks);
2574 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
2576 RouteTimeAxisView* rtv;
2578 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2580 if (rtv->is_track()) {
2582 /* no edits to destructive tracks */
2584 if (rtv->track()->destructive()) {
2588 if ((playlist = rtv->playlist()) != 0) {
2590 playlist->clear_changes ();
2592 /* XXX need to consider musical time selections here at some point */
2594 double speed = rtv->track()->speed();
2597 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
2599 sigc::connection c = rtv->view()->RegionViewAdded.connect (
2600 sigc::mem_fun(*this, &Editor::collect_new_region_view));
2602 latest_regionviews.clear ();
2604 playlist->partition ((framepos_t)((*t).start * speed),
2605 (framepos_t)((*t).end * speed), false);
2609 if (!latest_regionviews.empty()) {
2611 rtv->view()->foreach_regionview (sigc::bind (
2612 sigc::ptr_fun (add_if_covered),
2613 &(*t), &new_selection));
2616 begin_reversible_command (_("separate"));
2620 /* pick up changes to existing regions */
2622 vector<Command*> cmds;
2623 playlist->rdiff (cmds);
2624 _session->add_commands (cmds);
2626 /* pick up changes to the playlist itself (adds/removes)
2629 _session->add_command(new StatefulDiffCommand (playlist));
2638 selection->set (new_selection);
2639 set_mouse_mode (MouseObject);
2641 commit_reversible_command ();
2645 struct PlaylistState {
2646 boost::shared_ptr<Playlist> playlist;
2650 /** Take tracks from get_tracks_for_range_action and cut any regions
2651 * on those tracks so that the tracks are empty over the time
2655 Editor::separate_region_from_selection ()
2657 /* preferentially use *all* ranges in the time selection if we're in range mode
2658 to allow discontiguous operation, since get_edit_op_range() currently
2659 returns a single range.
2662 if (mouse_mode == MouseRange && !selection->time.empty()) {
2664 separate_regions_between (selection->time);
2671 if (get_edit_op_range (start, end)) {
2673 AudioRange ar (start, end, 1);
2677 separate_regions_between (ts);
2683 Editor::separate_region_from_punch ()
2685 Location* loc = _session->locations()->auto_punch_location();
2687 separate_regions_using_location (*loc);
2692 Editor::separate_region_from_loop ()
2694 Location* loc = _session->locations()->auto_loop_location();
2696 separate_regions_using_location (*loc);
2701 Editor::separate_regions_using_location (Location& loc)
2703 if (loc.is_mark()) {
2707 AudioRange ar (loc.start(), loc.end(), 1);
2712 separate_regions_between (ts);
2715 /** Separate regions under the selected region */
2717 Editor::separate_under_selected_regions ()
2719 vector<PlaylistState> playlists;
2723 rs = get_regions_from_selection_and_entered();
2725 if (!_session || rs.empty()) {
2729 begin_reversible_command (_("separate region under"));
2731 list<boost::shared_ptr<Region> > regions_to_remove;
2733 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2734 // we can't just remove the region(s) in this loop because
2735 // this removes them from the RegionSelection, and they thus
2736 // disappear from underneath the iterator, and the ++i above
2737 // SEGVs in a puzzling fashion.
2739 // so, first iterate over the regions to be removed from rs and
2740 // add them to the regions_to_remove list, and then
2741 // iterate over the list to actually remove them.
2743 regions_to_remove.push_back ((*i)->region());
2746 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
2748 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
2751 // is this check necessary?
2755 vector<PlaylistState>::iterator i;
2757 //only take state if this is a new playlist.
2758 for (i = playlists.begin(); i != playlists.end(); ++i) {
2759 if ((*i).playlist == playlist) {
2764 if (i == playlists.end()) {
2766 PlaylistState before;
2767 before.playlist = playlist;
2768 before.before = &playlist->get_state();
2770 playlist->freeze ();
2771 playlists.push_back(before);
2774 //Partition on the region bounds
2775 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
2777 //Re-add region that was just removed due to the partition operation
2778 playlist->add_region( (*rl), (*rl)->first_frame() );
2781 vector<PlaylistState>::iterator pl;
2783 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
2784 (*pl).playlist->thaw ();
2785 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
2788 commit_reversible_command ();
2792 Editor::crop_region_to_selection ()
2794 if (!selection->time.empty()) {
2796 crop_region_to (selection->time.start(), selection->time.end_frame());
2803 if (get_edit_op_range (start, end)) {
2804 crop_region_to (start, end);
2811 Editor::crop_region_to (framepos_t start, framepos_t end)
2813 vector<boost::shared_ptr<Playlist> > playlists;
2814 boost::shared_ptr<Playlist> playlist;
2817 if (selection->tracks.empty()) {
2818 ts = track_views.filter_to_unique_playlists();
2820 ts = selection->tracks.filter_to_unique_playlists ();
2823 sort_track_selection (ts);
2825 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2827 RouteTimeAxisView* rtv;
2829 if ((rtv = dynamic_cast<RouteTimeAxisView*> ((*i))) != 0) {
2831 boost::shared_ptr<Track> t = rtv->track();
2833 if (t != 0 && ! t->destructive()) {
2835 if ((playlist = rtv->playlist()) != 0) {
2836 playlists.push_back (playlist);
2842 if (playlists.empty()) {
2846 framepos_t the_start;
2850 begin_reversible_command (_("trim to selection"));
2852 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2854 boost::shared_ptr<Region> region;
2858 if ((region = (*i)->top_region_at(the_start)) == 0) {
2862 /* now adjust lengths to that we do the right thing
2863 if the selection extends beyond the region
2866 the_start = max (the_start, (framepos_t) region->position());
2867 if (max_framepos - the_start < region->length()) {
2868 the_end = the_start + region->length() - 1;
2870 the_end = max_framepos;
2872 the_end = min (end, the_end);
2873 cnt = the_end - the_start + 1;
2875 region->clear_changes ();
2876 region->trim_to (the_start, cnt);
2877 _session->add_command (new StatefulDiffCommand (region));
2880 commit_reversible_command ();
2884 Editor::region_fill_track ()
2886 RegionSelection rs = get_regions_from_selection_and_entered ();
2888 if (!_session || rs.empty()) {
2892 framepos_t const end = _session->current_end_frame ();
2894 begin_reversible_command (Operations::region_fill);
2896 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2898 boost::shared_ptr<Region> region ((*i)->region());
2900 boost::shared_ptr<Playlist> pl = region->playlist();
2902 if (end <= region->last_frame()) {
2906 double times = (double) (end - region->last_frame()) / (double) region->length();
2912 pl->clear_changes ();
2913 pl->add_region (RegionFactory::create (region, true), region->last_frame(), times);
2914 _session->add_command (new StatefulDiffCommand (pl));
2917 commit_reversible_command ();
2921 Editor::region_fill_selection ()
2923 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
2927 if (selection->time.empty()) {
2931 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2936 framepos_t start = selection->time[clicked_selection].start;
2937 framepos_t end = selection->time[clicked_selection].end;
2939 boost::shared_ptr<Playlist> playlist;
2941 if (selection->tracks.empty()) {
2945 framepos_t selection_length = end - start;
2946 float times = (float)selection_length / region->length();
2948 begin_reversible_command (Operations::fill_selection);
2950 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2952 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
2954 if ((playlist = (*i)->playlist()) == 0) {
2958 playlist->clear_changes ();
2959 playlist->add_region (RegionFactory::create (region, true), start, times);
2960 _session->add_command (new StatefulDiffCommand (playlist));
2963 commit_reversible_command ();
2967 Editor::set_region_sync_position ()
2969 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
2973 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
2975 bool in_command = false;
2977 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
2979 if (!(*r)->region()->covers (where)) {
2983 boost::shared_ptr<Region> region ((*r)->region());
2986 begin_reversible_command (_("set sync point"));
2990 region->clear_changes ();
2991 region->set_sync_position (where);
2992 _session->add_command(new StatefulDiffCommand (region));
2996 commit_reversible_command ();
3000 /** Remove the sync positions of the selection */
3002 Editor::remove_region_sync ()
3004 RegionSelection rs = get_regions_from_selection_and_entered ();
3010 begin_reversible_command (_("remove region sync"));
3012 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3014 (*i)->region()->clear_changes ();
3015 (*i)->region()->clear_sync_position ();
3016 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3019 commit_reversible_command ();
3023 Editor::naturalize_region ()
3025 RegionSelection rs = get_regions_from_selection_and_entered ();
3031 if (rs.size() > 1) {
3032 begin_reversible_command (_("move regions to original position"));
3034 begin_reversible_command (_("move region to original position"));
3037 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3038 (*i)->region()->clear_changes ();
3039 (*i)->region()->move_to_natural_position ();
3040 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3043 commit_reversible_command ();
3047 Editor::align_regions (RegionPoint what)
3049 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3055 begin_reversible_command (_("align selection"));
3057 framepos_t const position = get_preferred_edit_position ();
3059 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3060 align_region_internal ((*i)->region(), what, position);
3063 commit_reversible_command ();
3066 struct RegionSortByTime {
3067 bool operator() (const RegionView* a, const RegionView* b) {
3068 return a->region()->position() < b->region()->position();
3073 Editor::align_regions_relative (RegionPoint point)
3075 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3081 framepos_t const position = get_preferred_edit_position ();
3083 framepos_t distance = 0;
3087 list<RegionView*> sorted;
3088 rs.by_position (sorted);
3090 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3095 if (position > r->position()) {
3096 distance = position - r->position();
3098 distance = r->position() - position;
3104 if (position > r->last_frame()) {
3105 distance = position - r->last_frame();
3106 pos = r->position() + distance;
3108 distance = r->last_frame() - position;
3109 pos = r->position() - distance;
3115 pos = r->adjust_to_sync (position);
3116 if (pos > r->position()) {
3117 distance = pos - r->position();
3119 distance = r->position() - pos;
3125 if (pos == r->position()) {
3129 begin_reversible_command (_("align selection (relative)"));
3131 /* move first one specially */
3133 r->clear_changes ();
3134 r->set_position (pos);
3135 _session->add_command(new StatefulDiffCommand (r));
3137 /* move rest by the same amount */
3141 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3143 boost::shared_ptr<Region> region ((*i)->region());
3145 region->clear_changes ();
3148 region->set_position (region->position() + distance);
3150 region->set_position (region->position() - distance);
3153 _session->add_command(new StatefulDiffCommand (region));
3157 commit_reversible_command ();
3161 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3163 begin_reversible_command (_("align region"));
3164 align_region_internal (region, point, position);
3165 commit_reversible_command ();
3169 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3171 region->clear_changes ();
3175 region->set_position (region->adjust_to_sync (position));
3179 if (position > region->length()) {
3180 region->set_position (position - region->length());
3185 region->set_position (position);
3189 _session->add_command(new StatefulDiffCommand (region));
3193 Editor::trim_region_front ()
3199 Editor::trim_region_back ()
3201 trim_region (false);
3205 Editor::trim_region (bool front)
3207 framepos_t where = get_preferred_edit_position();
3208 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3214 begin_reversible_command (front ? _("trim front") : _("trim back"));
3216 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3217 if (!(*i)->region()->locked()) {
3219 (*i)->region()->clear_changes ();
3222 (*i)->region()->trim_front (where);
3224 (*i)->region()->trim_end (where);
3227 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3231 commit_reversible_command ();
3234 /** Trim the end of the selected regions to the position of the edit cursor */
3236 Editor::trim_region_to_loop ()
3238 Location* loc = _session->locations()->auto_loop_location();
3242 trim_region_to_location (*loc, _("trim to loop"));
3246 Editor::trim_region_to_punch ()
3248 Location* loc = _session->locations()->auto_punch_location();
3252 trim_region_to_location (*loc, _("trim to punch"));
3256 Editor::trim_region_to_location (const Location& loc, const char* str)
3258 RegionSelection rs = get_regions_from_selection_and_entered ();
3260 begin_reversible_command (str);
3262 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3263 RegionView* rv = (*x);
3265 /* require region to span proposed trim */
3266 switch (rv->region()->coverage (loc.start(), loc.end())) {
3267 case Evoral::OverlapInternal:
3273 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3282 if (tav->track() != 0) {
3283 speed = tav->track()->speed();
3286 start = session_frame_to_track_frame (loc.start(), speed);
3287 end = session_frame_to_track_frame (loc.end(), speed);
3289 rv->region()->clear_changes ();
3290 rv->region()->trim_to (start, (end - start));
3291 _session->add_command(new StatefulDiffCommand (rv->region()));
3294 commit_reversible_command ();
3298 Editor::trim_region_to_previous_region_end ()
3300 return trim_to_region(false);
3304 Editor::trim_region_to_next_region_start ()
3306 return trim_to_region(true);
3310 Editor::trim_to_region(bool forward)
3312 RegionSelection rs = get_regions_from_selection_and_entered ();
3314 begin_reversible_command (_("trim to region"));
3316 boost::shared_ptr<Region> next_region;
3318 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3320 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3326 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3334 if (atav->track() != 0) {
3335 speed = atav->track()->speed();
3339 boost::shared_ptr<Region> region = arv->region();
3340 boost::shared_ptr<Playlist> playlist (region->playlist());
3342 region->clear_changes ();
3346 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3352 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3353 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3357 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3363 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3365 arv->region_changed (ARDOUR::bounds_change);
3368 _session->add_command(new StatefulDiffCommand (region));
3371 commit_reversible_command ();
3375 Editor::unfreeze_route ()
3377 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3381 clicked_routeview->track()->unfreeze ();
3385 Editor::_freeze_thread (void* arg)
3387 return static_cast<Editor*>(arg)->freeze_thread ();
3391 Editor::freeze_thread ()
3393 /* create event pool because we may need to talk to the session */
3394 SessionEvent::create_per_thread_pool ("freeze events", 64);
3395 /* create per-thread buffers for process() tree to use */
3396 current_interthread_info->process_thread.init ();
3397 current_interthread_info->process_thread.get_buffers ();
3398 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3399 current_interthread_info->done = true;
3400 current_interthread_info->process_thread.drop_buffers();
3405 Editor::freeze_route ()
3411 /* stop transport before we start. this is important */
3413 _session->request_transport_speed (0.0);
3415 /* wait for just a little while, because the above call is asynchronous */
3419 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3423 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3425 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3426 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3428 d.set_title (_("Cannot freeze"));
3433 if (clicked_routeview->track()->has_external_redirects()) {
3434 MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
3435 "Freezing will only process the signal as far as the first send/insert/return."),
3436 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3438 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3439 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3440 d.set_title (_("Freeze Limits"));
3442 int response = d.run ();
3445 case Gtk::RESPONSE_CANCEL:
3452 InterThreadInfo itt;
3453 current_interthread_info = &itt;
3455 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3457 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3459 set_canvas_cursor (_cursors->wait);
3461 while (!itt.done && !itt.cancel) {
3462 gtk_main_iteration ();
3465 current_interthread_info = 0;
3466 set_canvas_cursor (current_canvas_cursor);
3470 Editor::bounce_range_selection (bool replace, bool enable_processing)
3472 if (selection->time.empty()) {
3476 TrackSelection views = selection->tracks;
3478 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3480 if (enable_processing) {
3482 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3484 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3486 _("You can't perform this operation because the processing of the signal "
3487 "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3488 "You can do this without processing, which is a different operation.")
3490 d.set_title (_("Cannot bounce"));
3497 framepos_t start = selection->time[clicked_selection].start;
3498 framepos_t end = selection->time[clicked_selection].end;
3499 framepos_t cnt = end - start + 1;
3501 begin_reversible_command (_("bounce range"));
3503 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3505 RouteTimeAxisView* rtv;
3507 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3511 boost::shared_ptr<Playlist> playlist;
3513 if ((playlist = rtv->playlist()) == 0) {
3517 InterThreadInfo itt;
3519 playlist->clear_changes ();
3520 playlist->clear_owned_changes ();
3522 boost::shared_ptr<Region> r;
3524 if (enable_processing) {
3525 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3527 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3535 list<AudioRange> ranges;
3536 ranges.push_back (AudioRange (start, start+cnt, 0));
3537 playlist->cut (ranges); // discard result
3538 playlist->add_region (r, start);
3541 vector<Command*> cmds;
3542 playlist->rdiff (cmds);
3543 _session->add_commands (cmds);
3545 _session->add_command (new StatefulDiffCommand (playlist));
3548 commit_reversible_command ();
3551 /** Delete selected regions, automation points or a time range */
3558 /** Cut selected regions, automation points or a time range */
3565 /** Copy selected regions, automation points or a time range */
3573 /** @return true if a Cut, Copy or Clear is possible */
3575 Editor::can_cut_copy () const
3577 switch (current_mouse_mode()) {
3580 if (!selection->regions.empty() || !selection->points.empty()) {
3586 if (!selection->time.empty()) {
3599 /** Cut, copy or clear selected regions, automation points or a time range.
3600 * @param op Operation (Cut, Copy or Clear)
3603 Editor::cut_copy (CutCopyOp op)
3605 /* only cancel selection if cut/copy is successful.*/
3611 opname = _("delete");
3620 opname = _("clear");
3624 /* if we're deleting something, and the mouse is still pressed,
3625 the thing we started a drag for will be gone when we release
3626 the mouse button(s). avoid this. see part 2 at the end of
3630 if (op == Delete || op == Cut || op == Clear) {
3631 if (_drags->active ()) {
3636 cut_buffer->clear ();
3638 if (entered_marker) {
3640 /* cut/delete op while pointing at a marker */
3643 Location* loc = find_location_from_marker (entered_marker, ignored);
3645 if (_session && loc) {
3646 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3653 if (internal_editing()) {
3655 switch (current_mouse_mode()) {
3668 /* we only want to cut regions if some are selected */
3670 if (doing_object_stuff()) {
3671 rs = get_regions_from_selection ();
3672 if (!rs.empty() || !selection->points.empty()) {
3674 begin_reversible_command (opname + _(" objects"));
3677 cut_copy_regions (op, rs);
3679 if (op == Cut || op == Delete) {
3680 selection->clear_regions ();
3684 if (!selection->points.empty()) {
3685 cut_copy_points (op);
3687 if (op == Cut || op == Delete) {
3688 selection->clear_points ();
3691 commit_reversible_command ();
3694 if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3695 /* don't cause suprises */
3700 if (doing_range_stuff()) {
3701 if (selection->time.empty()) {
3702 framepos_t start, end;
3703 if (!get_edit_op_range (start, end)) {
3706 selection->set (start, end);
3709 begin_reversible_command (opname + _(" range"));
3710 cut_copy_ranges (op);
3711 commit_reversible_command ();
3713 if (op == Cut || op == Delete) {
3714 selection->clear_time ();
3720 if (op == Delete || op == Cut || op == Clear) {
3725 struct AutomationRecord {
3726 AutomationRecord () : state (0) {}
3727 AutomationRecord (XMLNode* s) : state (s) {}
3729 XMLNode* state; ///< state before any operation
3730 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3733 /** Cut, copy or clear selected automation points.
3734 * @param op Operation (Cut, Copy or Clear)
3737 Editor::cut_copy_points (CutCopyOp op)
3739 if (selection->points.empty ()) {
3743 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3744 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3746 /* Keep a record of the AutomationLists that we end up using in this operation */
3747 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3750 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3751 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3752 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3753 if (lists.find (al) == lists.end ()) {
3754 /* We haven't seen this list yet, so make a record for it. This includes
3755 taking a copy of its current state, in case this is needed for undo later.
3757 lists[al] = AutomationRecord (&al->get_state ());
3761 if (op == Cut || op == Copy) {
3762 /* This operation will involve putting things in the cut buffer, so create an empty
3763 ControlList for each of our source lists to put the cut buffer data in.
3765 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3766 i->second.copy = i->first->create (i->first->parameter ());
3769 /* Add all selected points to the relevant copy ControlLists */
3770 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3771 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3772 AutomationList::const_iterator j = (*i)->model ();
3773 lists[al].copy->add ((*j)->when, (*j)->value);
3776 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3777 /* Correct this copy list so that it starts at time 0 */
3778 double const start = i->second.copy->front()->when;
3779 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3780 (*j)->when -= start;
3783 /* And add it to the cut buffer */
3784 cut_buffer->add (i->second.copy);
3788 if (op == Delete || op == Cut) {
3789 /* This operation needs to remove things from the main AutomationList, so do that now */
3791 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3792 i->first->freeze ();
3795 /* Remove each selected point from its AutomationList */
3796 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3797 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3798 al->erase ((*i)->model ());
3801 /* Thaw the lists and add undo records for them */
3802 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3803 boost::shared_ptr<AutomationList> al = i->first;
3805 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3810 /** Cut, copy or clear selected automation points.
3811 * @param op Operation (Cut, Copy or Clear)
3814 Editor::cut_copy_midi (CutCopyOp op)
3816 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3817 MidiRegionView* mrv = *i;
3818 mrv->cut_copy_clear (op);
3824 struct lt_playlist {
3825 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3826 return a.playlist < b.playlist;
3830 struct PlaylistMapping {
3832 boost::shared_ptr<Playlist> pl;
3834 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3837 /** Remove `clicked_regionview' */
3839 Editor::remove_clicked_region ()
3841 if (clicked_routeview == 0 || clicked_regionview == 0) {
3845 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3847 begin_reversible_command (_("remove region"));
3848 playlist->clear_changes ();
3849 playlist->clear_owned_changes ();
3850 playlist->remove_region (clicked_regionview->region());
3852 /* We might have removed regions, which alters other regions' layering_index,
3853 so we need to do a recursive diff here.
3855 vector<Command*> cmds;
3856 playlist->rdiff (cmds);
3857 _session->add_commands (cmds);
3859 _session->add_command(new StatefulDiffCommand (playlist));
3860 commit_reversible_command ();
3864 /** Remove the selected regions */
3866 Editor::remove_selected_regions ()
3868 RegionSelection rs = get_regions_from_selection_and_entered ();
3870 if (!_session || rs.empty()) {
3874 begin_reversible_command (_("remove region"));
3876 list<boost::shared_ptr<Region> > regions_to_remove;
3878 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3879 // we can't just remove the region(s) in this loop because
3880 // this removes them from the RegionSelection, and they thus
3881 // disappear from underneath the iterator, and the ++i above
3882 // SEGVs in a puzzling fashion.
3884 // so, first iterate over the regions to be removed from rs and
3885 // add them to the regions_to_remove list, and then
3886 // iterate over the list to actually remove them.
3888 regions_to_remove.push_back ((*i)->region());
3891 vector<boost::shared_ptr<Playlist> > playlists;
3893 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3895 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3898 // is this check necessary?
3902 /* get_regions_from_selection_and_entered() guarantees that
3903 the playlists involved are unique, so there is no need
3907 playlists.push_back (playlist);
3909 playlist->clear_changes ();
3910 playlist->clear_owned_changes ();
3911 playlist->freeze ();
3912 playlist->remove_region (*rl);
3915 vector<boost::shared_ptr<Playlist> >::iterator pl;
3917 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3920 /* We might have removed regions, which alters other regions' layering_index,
3921 so we need to do a recursive diff here.
3923 vector<Command*> cmds;
3924 (*pl)->rdiff (cmds);
3925 _session->add_commands (cmds);
3927 _session->add_command(new StatefulDiffCommand (*pl));
3930 commit_reversible_command ();
3933 /** Cut, copy or clear selected regions.
3934 * @param op Operation (Cut, Copy or Clear)
3937 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3939 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3940 a map when we want ordered access to both elements. i think.
3943 vector<PlaylistMapping> pmap;
3945 framepos_t first_position = max_framepos;
3947 typedef set<boost::shared_ptr<Playlist> > FreezeList;
3948 FreezeList freezelist;
3950 /* get ordering correct before we cut/copy */
3952 rs.sort_by_position_and_track ();
3954 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3956 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3958 if (op == Cut || op == Clear || op == Delete) {
3959 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3962 FreezeList::iterator fl;
3964 // only take state if this is a new playlist.
3965 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3971 if (fl == freezelist.end()) {
3972 pl->clear_changes();
3973 pl->clear_owned_changes ();
3975 freezelist.insert (pl);
3980 TimeAxisView* tv = &(*x)->get_time_axis_view();
3981 vector<PlaylistMapping>::iterator z;
3983 for (z = pmap.begin(); z != pmap.end(); ++z) {
3984 if ((*z).tv == tv) {
3989 if (z == pmap.end()) {
3990 pmap.push_back (PlaylistMapping (tv));
3994 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3996 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3999 /* region not yet associated with a playlist (e.g. unfinished
4006 TimeAxisView& tv = (*x)->get_time_axis_view();
4007 boost::shared_ptr<Playlist> npl;
4008 RegionSelection::iterator tmp;
4015 vector<PlaylistMapping>::iterator z;
4017 for (z = pmap.begin(); z != pmap.end(); ++z) {
4018 if ((*z).tv == &tv) {
4023 assert (z != pmap.end());
4026 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4034 boost::shared_ptr<Region> r = (*x)->region();
4035 boost::shared_ptr<Region> _xx;
4041 pl->remove_region (r);
4045 _xx = RegionFactory::create (r);
4046 npl->add_region (_xx, r->position() - first_position);
4047 pl->remove_region (r);
4051 /* copy region before adding, so we're not putting same object into two different playlists */
4052 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4056 pl->remove_region (r);
4065 list<boost::shared_ptr<Playlist> > foo;
4067 /* the pmap is in the same order as the tracks in which selected regions occured */
4069 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4072 foo.push_back ((*i).pl);
4077 cut_buffer->set (foo);
4081 _last_cut_copy_source_track = 0;
4083 _last_cut_copy_source_track = pmap.front().tv;
4087 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4090 /* We might have removed regions, which alters other regions' layering_index,
4091 so we need to do a recursive diff here.
4093 vector<Command*> cmds;
4094 (*pl)->rdiff (cmds);
4095 _session->add_commands (cmds);
4097 _session->add_command (new StatefulDiffCommand (*pl));
4102 Editor::cut_copy_ranges (CutCopyOp op)
4104 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4106 /* Sort the track selection now, so that it if is used, the playlists
4107 selected by the calls below to cut_copy_clear are in the order that
4108 their tracks appear in the editor. This makes things like paste
4109 of ranges work properly.
4112 sort_track_selection (ts);
4115 if (!entered_track) {
4118 ts.push_back (entered_track);
4121 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4122 (*i)->cut_copy_clear (*selection, op);
4127 Editor::paste (float times, bool from_context)
4129 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4131 paste_internal (get_preferred_edit_position (false, from_context), times);
4135 Editor::mouse_paste ()
4140 if (!mouse_frame (where, ignored)) {
4145 paste_internal (where, 1);
4149 Editor::paste_internal (framepos_t position, float times)
4151 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4153 if (internal_editing()) {
4154 if (cut_buffer->midi_notes.empty()) {
4158 if (cut_buffer->empty()) {
4163 if (position == max_framepos) {
4164 position = get_preferred_edit_position();
4165 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4169 TrackViewList::iterator i;
4172 /* get everything in the correct order */
4174 if (_edit_point == Editing::EditAtMouse && entered_track) {
4175 /* With the mouse edit point, paste onto the track under the mouse */
4176 ts.push_back (entered_track);
4177 } else if (!selection->tracks.empty()) {
4178 /* Otherwise, if there are some selected tracks, paste to them */
4179 ts = selection->tracks.filter_to_unique_playlists ();
4180 sort_track_selection (ts);
4181 } else if (_last_cut_copy_source_track) {
4182 /* Otherwise paste to the track that the cut/copy came from;
4183 see discussion in mantis #3333.
4185 ts.push_back (_last_cut_copy_source_track);
4188 if (internal_editing ()) {
4190 /* undo/redo is handled by individual tracks/regions */
4192 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4195 RegionSelection::iterator r;
4196 MidiNoteSelection::iterator cb;
4198 get_regions_at (rs, position, ts);
4200 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4201 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4202 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4204 mrv->paste (position, times, **cb);
4212 /* we do redo (do you do voodoo?) */
4214 begin_reversible_command (Operations::paste);
4216 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4217 (*i)->paste (position, times, *cut_buffer, nth);
4220 commit_reversible_command ();
4225 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4227 boost::shared_ptr<Playlist> playlist;
4228 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4229 RegionSelection foo;
4231 framepos_t const start_frame = regions.start ();
4232 framepos_t const end_frame = regions.end_frame ();
4234 begin_reversible_command (Operations::duplicate_region);
4236 selection->clear_regions ();
4238 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4240 boost::shared_ptr<Region> r ((*i)->region());
4242 TimeAxisView& tv = (*i)->get_time_axis_view();
4243 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4244 latest_regionviews.clear ();
4245 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4247 playlist = (*i)->region()->playlist();
4248 playlist->clear_changes ();
4249 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4250 _session->add_command(new StatefulDiffCommand (playlist));
4254 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4257 commit_reversible_command ();
4260 selection->set (foo);
4265 Editor::duplicate_selection (float times)
4267 if (selection->time.empty() || selection->tracks.empty()) {
4271 boost::shared_ptr<Playlist> playlist;
4272 vector<boost::shared_ptr<Region> > new_regions;
4273 vector<boost::shared_ptr<Region> >::iterator ri;
4275 create_region_from_selection (new_regions);
4277 if (new_regions.empty()) {
4281 begin_reversible_command (_("duplicate selection"));
4283 ri = new_regions.begin();
4285 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4287 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4288 if ((playlist = (*i)->playlist()) == 0) {
4291 playlist->clear_changes ();
4292 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4293 _session->add_command (new StatefulDiffCommand (playlist));
4296 if (ri == new_regions.end()) {
4301 commit_reversible_command ();
4304 /** Reset all selected points to the relevant default value */
4306 Editor::reset_point_selection ()
4308 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4309 ARDOUR::AutomationList::iterator j = (*i)->model ();
4310 (*j)->value = (*i)->line().the_list()->default_value ();
4315 Editor::center_playhead ()
4317 float page = _canvas_width * frames_per_unit;
4318 center_screen_internal (playhead_cursor->current_frame, page);
4322 Editor::center_edit_point ()
4324 float page = _canvas_width * frames_per_unit;
4325 center_screen_internal (get_preferred_edit_position(), page);
4328 /** Caller must begin and commit a reversible command */
4330 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4332 playlist->clear_changes ();
4334 _session->add_command (new StatefulDiffCommand (playlist));
4338 Editor::nudge_track (bool use_edit, bool forwards)
4340 boost::shared_ptr<Playlist> playlist;
4341 framepos_t distance;
4342 framepos_t next_distance;
4346 start = get_preferred_edit_position();
4351 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4355 if (selection->tracks.empty()) {
4359 begin_reversible_command (_("nudge track"));
4361 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4363 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4365 if ((playlist = (*i)->playlist()) == 0) {
4369 playlist->clear_changes ();
4370 playlist->clear_owned_changes ();
4372 playlist->nudge_after (start, distance, forwards);
4374 vector<Command*> cmds;
4376 playlist->rdiff (cmds);
4377 _session->add_commands (cmds);
4379 _session->add_command (new StatefulDiffCommand (playlist));
4382 commit_reversible_command ();
4386 Editor::remove_last_capture ()
4388 vector<string> choices;
4395 if (Config->get_verify_remove_last_capture()) {
4396 prompt = _("Do you really want to destroy the last capture?"
4397 "\n(This is destructive and cannot be undone)");
4399 choices.push_back (_("No, do nothing."));
4400 choices.push_back (_("Yes, destroy it."));
4402 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4404 if (prompter.run () == 1) {
4405 _session->remove_last_capture ();
4406 _regions->redisplay ();
4410 _session->remove_last_capture();
4411 _regions->redisplay ();
4416 Editor::normalize_region ()
4422 RegionSelection rs = get_regions_from_selection_and_entered ();
4428 NormalizeDialog dialog (rs.size() > 1);
4430 if (dialog.run () == RESPONSE_CANCEL) {
4434 set_canvas_cursor (_cursors->wait);
4437 /* XXX: should really only count audio regions here */
4438 int const regions = rs.size ();
4440 /* Make a list of the selected audio regions' maximum amplitudes, and also
4441 obtain the maximum amplitude of them all.
4443 list<double> max_amps;
4445 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4446 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4448 dialog.descend (1.0 / regions);
4449 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4452 /* the user cancelled the operation */
4453 set_canvas_cursor (current_canvas_cursor);
4457 max_amps.push_back (a);
4458 max_amp = max (max_amp, a);
4463 begin_reversible_command (_("normalize"));
4465 list<double>::const_iterator a = max_amps.begin ();
4467 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4468 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4473 arv->region()->clear_changes ();
4475 double const amp = dialog.normalize_individually() ? *a : max_amp;
4477 arv->audio_region()->normalize (amp, dialog.target ());
4478 _session->add_command (new StatefulDiffCommand (arv->region()));
4483 commit_reversible_command ();
4484 set_canvas_cursor (current_canvas_cursor);
4489 Editor::reset_region_scale_amplitude ()
4495 RegionSelection rs = get_regions_from_selection_and_entered ();
4501 begin_reversible_command ("reset gain");
4503 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4504 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4507 arv->region()->clear_changes ();
4508 arv->audio_region()->set_scale_amplitude (1.0f);
4509 _session->add_command (new StatefulDiffCommand (arv->region()));
4512 commit_reversible_command ();
4516 Editor::adjust_region_gain (bool up)
4518 RegionSelection rs = get_regions_from_selection_and_entered ();
4520 if (!_session || rs.empty()) {
4524 begin_reversible_command ("adjust region gain");
4526 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4527 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4532 arv->region()->clear_changes ();
4534 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4542 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4543 _session->add_command (new StatefulDiffCommand (arv->region()));
4546 commit_reversible_command ();
4551 Editor::reverse_region ()
4557 Reverse rev (*_session);
4558 apply_filter (rev, _("reverse regions"));
4562 Editor::strip_region_silence ()
4568 RegionSelection rs = get_regions_from_selection_and_entered ();
4574 std::list<RegionView*> audio_only;
4576 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4577 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4579 audio_only.push_back (arv);
4583 StripSilenceDialog d (_session, audio_only);
4584 int const r = d.run ();
4588 if (r == Gtk::RESPONSE_OK) {
4589 ARDOUR::AudioIntervalMap silences;
4590 d.silences (silences);
4591 StripSilence s (*_session, silences, d.fade_length());
4592 apply_filter (s, _("strip silence"), &d);
4597 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4599 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4600 mrv.selection_as_notelist (selected, true);
4602 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4603 v.push_back (selected);
4605 framepos_t pos_frames = mrv.midi_region()->position();
4606 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4608 return op (mrv.midi_region()->model(), pos_beats, v);
4612 Editor::apply_midi_note_edit_op (MidiOperator& op)
4616 RegionSelection rs = get_regions_from_selection_and_entered ();
4622 begin_reversible_command (op.name ());
4624 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4625 RegionSelection::iterator tmp = r;
4628 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4631 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4634 _session->add_command (cmd);
4641 commit_reversible_command ();
4645 Editor::fork_region ()
4647 RegionSelection rs = get_regions_from_selection_and_entered ();
4653 begin_reversible_command (_("Fork Region(s)"));
4655 set_canvas_cursor (_cursors->wait);
4658 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4659 RegionSelection::iterator tmp = r;
4662 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4665 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4666 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4668 playlist->clear_changes ();
4669 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4670 _session->add_command(new StatefulDiffCommand (playlist));
4676 commit_reversible_command ();
4678 set_canvas_cursor (current_canvas_cursor);
4682 Editor::quantize_region ()
4684 int selected_midi_region_cnt = 0;
4690 RegionSelection rs = get_regions_from_selection_and_entered ();
4696 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4697 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4699 selected_midi_region_cnt++;
4703 if (selected_midi_region_cnt == 0) {
4707 QuantizeDialog* qd = new QuantizeDialog (*this);
4710 const int r = qd->run ();
4713 if (r == Gtk::RESPONSE_OK) {
4714 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4715 qd->start_grid_size(), qd->end_grid_size(),
4716 qd->strength(), qd->swing(), qd->threshold());
4718 apply_midi_note_edit_op (quant);
4723 Editor::insert_patch_change (bool from_context)
4725 RegionSelection rs = get_regions_from_selection_and_entered ();
4731 const framepos_t p = get_preferred_edit_position (false, from_context);
4733 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4734 there may be more than one, but the PatchChangeDialog can only offer
4735 one set of patch menus.
4737 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4739 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4740 PatchChangeDialog d (0, _session, empty, first->model_name(), first->custom_device_mode(), Gtk::Stock::ADD);
4742 if (d.run() == RESPONSE_CANCEL) {
4746 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4747 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4749 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4750 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4757 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4759 RegionSelection rs = get_regions_from_selection_and_entered ();
4765 begin_reversible_command (command);
4767 set_canvas_cursor (_cursors->wait);
4771 int const N = rs.size ();
4773 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4774 RegionSelection::iterator tmp = r;
4777 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4779 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4782 progress->descend (1.0 / N);
4785 if (arv->audio_region()->apply (filter, progress) == 0) {
4787 playlist->clear_changes ();
4788 playlist->clear_owned_changes ();
4790 if (filter.results.empty ()) {
4792 /* no regions returned; remove the old one */
4793 playlist->remove_region (arv->region ());
4797 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4799 /* first region replaces the old one */
4800 playlist->replace_region (arv->region(), *res, (*res)->position());
4804 while (res != filter.results.end()) {
4805 playlist->add_region (*res, (*res)->position());
4811 /* We might have removed regions, which alters other regions' layering_index,
4812 so we need to do a recursive diff here.
4814 vector<Command*> cmds;
4815 playlist->rdiff (cmds);
4816 _session->add_commands (cmds);
4818 _session->add_command(new StatefulDiffCommand (playlist));
4824 progress->ascend ();
4832 commit_reversible_command ();
4835 set_canvas_cursor (current_canvas_cursor);
4839 Editor::external_edit_region ()
4845 Editor::reset_region_gain_envelopes ()
4847 RegionSelection rs = get_regions_from_selection_and_entered ();
4849 if (!_session || rs.empty()) {
4853 _session->begin_reversible_command (_("reset region gain"));
4855 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4856 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4858 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4859 XMLNode& before (alist->get_state());
4861 arv->audio_region()->set_default_envelope ();
4862 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4866 _session->commit_reversible_command ();
4870 Editor::set_region_gain_visibility (RegionView* rv)
4872 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4874 arv->update_envelope_visibility();
4879 Editor::set_gain_envelope_visibility ()
4885 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4886 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4888 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4894 Editor::toggle_gain_envelope_active ()
4896 if (_ignore_region_action) {
4900 RegionSelection rs = get_regions_from_selection_and_entered ();
4902 if (!_session || rs.empty()) {
4906 _session->begin_reversible_command (_("region gain envelope active"));
4908 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4909 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4911 arv->region()->clear_changes ();
4912 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4913 _session->add_command (new StatefulDiffCommand (arv->region()));
4917 _session->commit_reversible_command ();
4921 Editor::toggle_region_lock ()
4923 if (_ignore_region_action) {
4927 RegionSelection rs = get_regions_from_selection_and_entered ();
4929 if (!_session || rs.empty()) {
4933 _session->begin_reversible_command (_("toggle region lock"));
4935 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4936 (*i)->region()->clear_changes ();
4937 (*i)->region()->set_locked (!(*i)->region()->locked());
4938 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4941 _session->commit_reversible_command ();
4945 Editor::toggle_region_lock_style ()
4947 if (_ignore_region_action) {
4951 RegionSelection rs = get_regions_from_selection_and_entered ();
4953 if (!_session || rs.empty()) {
4957 _session->begin_reversible_command (_("region lock style"));
4959 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4960 (*i)->region()->clear_changes ();
4961 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4962 (*i)->region()->set_position_lock_style (ns);
4963 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4966 _session->commit_reversible_command ();
4970 Editor::toggle_opaque_region ()
4972 if (_ignore_region_action) {
4976 RegionSelection rs = get_regions_from_selection_and_entered ();
4978 if (!_session || rs.empty()) {
4982 _session->begin_reversible_command (_("change region opacity"));
4984 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4985 (*i)->region()->clear_changes ();
4986 (*i)->region()->set_opaque (!(*i)->region()->opaque());
4987 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4990 _session->commit_reversible_command ();
4994 Editor::toggle_record_enable ()
4996 bool new_state = false;
4998 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4999 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5002 if (!rtav->is_track())
5006 new_state = !rtav->track()->record_enabled();
5010 rtav->track()->set_record_enabled (new_state, this);
5015 Editor::toggle_solo ()
5017 bool new_state = false;
5019 boost::shared_ptr<RouteList> rl (new RouteList);
5021 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5022 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5029 new_state = !rtav->route()->soloed ();
5033 rl->push_back (rtav->route());
5036 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5040 Editor::toggle_mute ()
5042 bool new_state = false;
5044 boost::shared_ptr<RouteList> rl (new RouteList);
5046 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5047 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5054 new_state = !rtav->route()->muted();
5058 rl->push_back (rtav->route());
5061 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5065 Editor::toggle_solo_isolate ()
5070 Editor::set_fade_length (bool in)
5072 RegionSelection rs = get_regions_from_selection_and_entered ();
5078 /* we need a region to measure the offset from the start */
5080 RegionView* rv = rs.front ();
5082 framepos_t pos = get_preferred_edit_position();
5086 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5087 /* edit point is outside the relevant region */
5092 if (pos <= rv->region()->position()) {
5096 len = pos - rv->region()->position();
5097 cmd = _("set fade in length");
5099 if (pos >= rv->region()->last_frame()) {
5103 len = rv->region()->last_frame() - pos;
5104 cmd = _("set fade out length");
5107 begin_reversible_command (cmd);
5109 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5110 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5116 boost::shared_ptr<AutomationList> alist;
5118 alist = tmp->audio_region()->fade_in();
5120 alist = tmp->audio_region()->fade_out();
5123 XMLNode &before = alist->get_state();
5126 tmp->audio_region()->set_fade_in_length (len);
5127 tmp->audio_region()->set_fade_in_active (true);
5129 tmp->audio_region()->set_fade_out_length (len);
5130 tmp->audio_region()->set_fade_out_active (true);
5133 XMLNode &after = alist->get_state();
5134 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5137 commit_reversible_command ();
5141 Editor::set_fade_in_shape (FadeShape shape)
5143 RegionSelection rs = get_regions_from_selection_and_entered ();
5149 begin_reversible_command (_("set fade in shape"));
5151 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5152 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5158 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5159 XMLNode &before = alist->get_state();
5161 tmp->audio_region()->set_fade_in_shape (shape);
5163 XMLNode &after = alist->get_state();
5164 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5167 commit_reversible_command ();
5172 Editor::set_fade_out_shape (FadeShape shape)
5174 RegionSelection rs = get_regions_from_selection_and_entered ();
5180 begin_reversible_command (_("set fade out shape"));
5182 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5183 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5189 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5190 XMLNode &before = alist->get_state();
5192 tmp->audio_region()->set_fade_out_shape (shape);
5194 XMLNode &after = alist->get_state();
5195 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5198 commit_reversible_command ();
5202 Editor::set_fade_in_active (bool yn)
5204 RegionSelection rs = get_regions_from_selection_and_entered ();
5210 begin_reversible_command (_("set fade in active"));
5212 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5213 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5220 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5222 ar->clear_changes ();
5223 ar->set_fade_in_active (yn);
5224 _session->add_command (new StatefulDiffCommand (ar));
5227 commit_reversible_command ();
5231 Editor::set_fade_out_active (bool yn)
5233 RegionSelection rs = get_regions_from_selection_and_entered ();
5239 begin_reversible_command (_("set fade out active"));
5241 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5242 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5248 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5250 ar->clear_changes ();
5251 ar->set_fade_out_active (yn);
5252 _session->add_command(new StatefulDiffCommand (ar));
5255 commit_reversible_command ();
5259 Editor::toggle_region_fades (int dir)
5261 if (_ignore_region_action) {
5265 boost::shared_ptr<AudioRegion> ar;
5268 RegionSelection rs = get_regions_from_selection_and_entered ();
5274 RegionSelection::iterator i;
5275 for (i = rs.begin(); i != rs.end(); ++i) {
5276 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5278 yn = ar->fade_out_active ();
5280 yn = ar->fade_in_active ();
5286 if (i == rs.end()) {
5290 /* XXX should this undo-able? */
5292 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5293 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5296 if (dir == 1 || dir == 0) {
5297 ar->set_fade_in_active (!yn);
5300 if (dir == -1 || dir == 0) {
5301 ar->set_fade_out_active (!yn);
5307 /** Update region fade visibility after its configuration has been changed */
5309 Editor::update_region_fade_visibility ()
5311 bool _fade_visibility = _session->config.get_show_region_fades ();
5313 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5314 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5316 if (_fade_visibility) {
5317 v->audio_view()->show_all_fades ();
5319 v->audio_view()->hide_all_fades ();
5326 Editor::set_edit_point ()
5331 if (!mouse_frame (where, ignored)) {
5337 if (selection->markers.empty()) {
5339 mouse_add_new_marker (where);
5344 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5347 loc->move_to (where);
5353 Editor::set_playhead_cursor ()
5355 if (entered_marker) {
5356 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5361 if (!mouse_frame (where, ignored)) {
5368 _session->request_locate (where, _session->transport_rolling());
5374 Editor::split_region ()
5376 if (((mouse_mode == MouseRange) ||
5377 (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5378 !selection->time.empty()) {
5379 separate_regions_between (selection->time);
5383 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5385 framepos_t where = get_preferred_edit_position ();
5391 split_regions_at (where, rs);
5394 struct EditorOrderRouteSorter {
5395 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5396 /* use of ">" forces the correct sort order */
5397 return a->order_key ("editor") < b->order_key ("editor");
5402 Editor::select_next_route()
5404 if (selection->tracks.empty()) {
5405 selection->set (track_views.front());
5409 TimeAxisView* current = selection->tracks.front();
5413 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5414 if (*i == current) {
5416 if (i != track_views.end()) {
5419 current = (*(track_views.begin()));
5420 //selection->set (*(track_views.begin()));
5425 rui = dynamic_cast<RouteUI *>(current);
5426 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5428 selection->set(current);
5430 ensure_track_visible(current);
5434 Editor::select_prev_route()
5436 if (selection->tracks.empty()) {
5437 selection->set (track_views.front());
5441 TimeAxisView* current = selection->tracks.front();
5445 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5446 if (*i == current) {
5448 if (i != track_views.rend()) {
5451 current = *(track_views.rbegin());
5456 rui = dynamic_cast<RouteUI *>(current);
5457 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5459 selection->set (current);
5461 ensure_track_visible(current);
5465 Editor::ensure_track_visible(TimeAxisView *track)
5467 if (track->hidden())
5470 double const current_view_min_y = vertical_adjustment.get_value();
5471 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5473 double const track_min_y = track->y_position ();
5474 double const track_max_y = track->y_position () + track->effective_height ();
5476 if (track_min_y >= current_view_min_y &&
5477 track_max_y <= current_view_max_y) {
5483 if (track_min_y < current_view_min_y) {
5484 // Track is above the current view
5485 new_value = track_min_y;
5487 // Track is below the current view
5488 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5491 vertical_adjustment.set_value(new_value);
5495 Editor::set_loop_from_selection (bool play)
5497 if (_session == 0 || selection->time.empty()) {
5501 framepos_t start = selection->time[clicked_selection].start;
5502 framepos_t end = selection->time[clicked_selection].end;
5504 set_loop_range (start, end, _("set loop range from selection"));
5507 _session->request_play_loop (true);
5508 _session->request_locate (start, true);
5513 Editor::set_loop_from_edit_range (bool play)
5515 if (_session == 0) {
5522 if (!get_edit_op_range (start, end)) {
5526 set_loop_range (start, end, _("set loop range from edit range"));
5529 _session->request_play_loop (true);
5530 _session->request_locate (start, true);
5535 Editor::set_loop_from_region (bool play)
5537 framepos_t start = max_framepos;
5540 RegionSelection rs = get_regions_from_selection_and_entered ();
5546 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5547 if ((*i)->region()->position() < start) {
5548 start = (*i)->region()->position();
5550 if ((*i)->region()->last_frame() + 1 > end) {
5551 end = (*i)->region()->last_frame() + 1;
5555 set_loop_range (start, end, _("set loop range from region"));
5558 _session->request_play_loop (true);
5559 _session->request_locate (start, true);
5564 Editor::set_punch_from_selection ()
5566 if (_session == 0 || selection->time.empty()) {
5570 framepos_t start = selection->time[clicked_selection].start;
5571 framepos_t end = selection->time[clicked_selection].end;
5573 set_punch_range (start, end, _("set punch range from selection"));
5577 Editor::set_punch_from_edit_range ()
5579 if (_session == 0) {
5586 if (!get_edit_op_range (start, end)) {
5590 set_punch_range (start, end, _("set punch range from edit range"));
5594 Editor::set_punch_from_region ()
5596 framepos_t start = max_framepos;
5599 RegionSelection rs = get_regions_from_selection_and_entered ();
5605 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5606 if ((*i)->region()->position() < start) {
5607 start = (*i)->region()->position();
5609 if ((*i)->region()->last_frame() + 1 > end) {
5610 end = (*i)->region()->last_frame() + 1;
5614 set_punch_range (start, end, _("set punch range from region"));
5618 Editor::pitch_shift_region ()
5620 RegionSelection rs = get_regions_from_selection_and_entered ();
5622 RegionSelection audio_rs;
5623 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5624 if (dynamic_cast<AudioRegionView*> (*i)) {
5625 audio_rs.push_back (*i);
5629 if (audio_rs.empty()) {
5633 pitch_shift (audio_rs, 1.2);
5637 Editor::transpose_region ()
5639 RegionSelection rs = get_regions_from_selection_and_entered ();
5641 list<MidiRegionView*> midi_region_views;
5642 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5643 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5645 midi_region_views.push_back (mrv);
5650 int const r = d.run ();
5651 if (r != RESPONSE_ACCEPT) {
5655 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5656 (*i)->midi_region()->transpose (d.semitones ());
5661 Editor::set_tempo_from_region ()
5663 RegionSelection rs = get_regions_from_selection_and_entered ();
5665 if (!_session || rs.empty()) {
5669 RegionView* rv = rs.front();
5671 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5675 Editor::use_range_as_bar ()
5677 framepos_t start, end;
5678 if (get_edit_op_range (start, end)) {
5679 define_one_bar (start, end);
5684 Editor::define_one_bar (framepos_t start, framepos_t end)
5686 framepos_t length = end - start;
5688 const Meter& m (_session->tempo_map().meter_at (start));
5690 /* length = 1 bar */
5692 /* now we want frames per beat.
5693 we have frames per bar, and beats per bar, so ...
5696 /* XXXX METER MATH */
5698 double frames_per_beat = length / m.divisions_per_bar();
5700 /* beats per minute = */
5702 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5704 /* now decide whether to:
5706 (a) set global tempo
5707 (b) add a new tempo marker
5711 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5713 bool do_global = false;
5715 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5717 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5718 at the start, or create a new marker
5721 vector<string> options;
5722 options.push_back (_("Cancel"));
5723 options.push_back (_("Add new marker"));
5724 options.push_back (_("Set global tempo"));
5727 _("Define one bar"),
5728 _("Do you want to set the global tempo or add a new tempo marker?"),
5732 c.set_default_response (2);
5748 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5749 if the marker is at the region starter, change it, otherwise add
5754 begin_reversible_command (_("set tempo from region"));
5755 XMLNode& before (_session->tempo_map().get_state());
5758 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5759 } else if (t.frame() == start) {
5760 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5762 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5765 XMLNode& after (_session->tempo_map().get_state());
5767 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5768 commit_reversible_command ();
5772 Editor::split_region_at_transients ()
5774 AnalysisFeatureList positions;
5776 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 if (!_session || rs.empty()) {
5782 _session->begin_reversible_command (_("split regions"));
5784 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5786 RegionSelection::iterator tmp;
5791 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5793 if (ar && (ar->get_transients (positions) == 0)) {
5794 split_region_at_points ((*i)->region(), positions, true);
5801 _session->commit_reversible_command ();
5806 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5808 bool use_rhythmic_rodent = false;
5810 boost::shared_ptr<Playlist> pl = r->playlist();
5812 list<boost::shared_ptr<Region> > new_regions;
5818 if (positions.empty()) {
5823 if (positions.size() > 20 && can_ferret) {
5824 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);
5825 MessageDialog msg (msgstr,
5828 Gtk::BUTTONS_OK_CANCEL);
5831 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5832 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5834 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5837 msg.set_title (_("Excessive split?"));
5840 int response = msg.run();
5846 case RESPONSE_APPLY:
5847 use_rhythmic_rodent = true;
5854 if (use_rhythmic_rodent) {
5855 show_rhythm_ferret ();
5859 AnalysisFeatureList::const_iterator x;
5861 pl->clear_changes ();
5862 pl->clear_owned_changes ();
5864 x = positions.begin();
5866 if (x == positions.end()) {
5871 pl->remove_region (r);
5875 while (x != positions.end()) {
5877 /* deal with positons that are out of scope of present region bounds */
5878 if (*x <= 0 || *x > r->length()) {
5883 /* file start = original start + how far we from the initial position ?
5886 framepos_t file_start = r->start() + pos;
5888 /* length = next position - current position
5891 framepos_t len = (*x) - pos;
5893 /* XXX we do we really want to allow even single-sample regions?
5894 shouldn't we have some kind of lower limit on region size?
5903 if (RegionFactory::region_name (new_name, r->name())) {
5907 /* do NOT announce new regions 1 by one, just wait till they are all done */
5911 plist.add (ARDOUR::Properties::start, file_start);
5912 plist.add (ARDOUR::Properties::length, len);
5913 plist.add (ARDOUR::Properties::name, new_name);
5914 plist.add (ARDOUR::Properties::layer, 0);
5916 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5917 /* because we set annouce to false, manually add the new region to the
5920 RegionFactory::map_add (nr);
5922 pl->add_region (nr, r->position() + pos);
5925 new_regions.push_front(nr);
5934 RegionFactory::region_name (new_name, r->name());
5936 /* Add the final region */
5939 plist.add (ARDOUR::Properties::start, r->start() + pos);
5940 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5941 plist.add (ARDOUR::Properties::name, new_name);
5942 plist.add (ARDOUR::Properties::layer, 0);
5944 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5945 /* because we set annouce to false, manually add the new region to the
5948 RegionFactory::map_add (nr);
5949 pl->add_region (nr, r->position() + pos);
5952 new_regions.push_front(nr);
5957 /* We might have removed regions, which alters other regions' layering_index,
5958 so we need to do a recursive diff here.
5960 vector<Command*> cmds;
5962 _session->add_commands (cmds);
5964 _session->add_command (new StatefulDiffCommand (pl));
5968 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5969 set_selected_regionview_from_region_list ((*i), Selection::Add);
5975 Editor::place_transient()
5981 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5987 framepos_t where = get_preferred_edit_position();
5989 _session->begin_reversible_command (_("place transient"));
5991 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5992 framepos_t position = (*r)->region()->position();
5993 (*r)->region()->add_transient(where - position);
5996 _session->commit_reversible_command ();
6000 Editor::remove_transient(ArdourCanvas::Item* item)
6006 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6009 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6010 _arv->remove_transient (*(float*) _line->get_data ("position"));
6014 Editor::snap_regions_to_grid ()
6016 list <boost::shared_ptr<Playlist > > used_playlists;
6018 RegionSelection rs = get_regions_from_selection_and_entered ();
6020 if (!_session || rs.empty()) {
6024 _session->begin_reversible_command (_("snap regions to grid"));
6026 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6028 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6030 if (!pl->frozen()) {
6031 /* we haven't seen this playlist before */
6033 /* remember used playlists so we can thaw them later */
6034 used_playlists.push_back(pl);
6038 framepos_t start_frame = (*r)->region()->first_frame ();
6039 snap_to (start_frame);
6040 (*r)->region()->set_position (start_frame);
6043 while (used_playlists.size() > 0) {
6044 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6046 used_playlists.pop_front();
6049 _session->commit_reversible_command ();
6053 Editor::close_region_gaps ()
6055 list <boost::shared_ptr<Playlist > > used_playlists;
6057 RegionSelection rs = get_regions_from_selection_and_entered ();
6059 if (!_session || rs.empty()) {
6063 Dialog dialog (_("Close Region Gaps"));
6066 table.set_spacings (12);
6067 table.set_border_width (12);
6068 Label* l = manage (left_aligned_label (_("Crossfade length")));
6069 table.attach (*l, 0, 1, 0, 1);
6071 SpinButton spin_crossfade (1, 0);
6072 spin_crossfade.set_range (0, 15);
6073 spin_crossfade.set_increments (1, 1);
6074 spin_crossfade.set_value (5);
6075 table.attach (spin_crossfade, 1, 2, 0, 1);
6077 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6079 l = manage (left_aligned_label (_("Pull-back length")));
6080 table.attach (*l, 0, 1, 1, 2);
6082 SpinButton spin_pullback (1, 0);
6083 spin_pullback.set_range (0, 100);
6084 spin_pullback.set_increments (1, 1);
6085 spin_pullback.set_value(30);
6086 table.attach (spin_pullback, 1, 2, 1, 2);
6088 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6090 dialog.get_vbox()->pack_start (table);
6091 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6092 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6095 if (dialog.run () == RESPONSE_CANCEL) {
6099 framepos_t crossfade_len = spin_crossfade.get_value();
6100 framepos_t pull_back_frames = spin_pullback.get_value();
6102 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6103 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6105 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6107 _session->begin_reversible_command (_("close region gaps"));
6110 boost::shared_ptr<Region> last_region;
6112 rs.sort_by_position_and_track();
6114 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6116 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6118 if (!pl->frozen()) {
6119 /* we haven't seen this playlist before */
6121 /* remember used playlists so we can thaw them later */
6122 used_playlists.push_back(pl);
6126 framepos_t position = (*r)->region()->position();
6128 if (idx == 0 || position < last_region->position()){
6129 last_region = (*r)->region();
6134 (*r)->region()->trim_front( (position - pull_back_frames));
6135 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6137 last_region = (*r)->region();
6142 while (used_playlists.size() > 0) {
6143 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6145 used_playlists.pop_front();
6148 _session->commit_reversible_command ();
6152 Editor::tab_to_transient (bool forward)
6154 AnalysisFeatureList positions;
6156 RegionSelection rs = get_regions_from_selection_and_entered ();
6162 framepos_t pos = _session->audible_frame ();
6164 if (!selection->tracks.empty()) {
6166 /* don't waste time searching for transients in duplicate playlists.
6169 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6171 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6173 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6176 boost::shared_ptr<Track> tr = rtv->track();
6178 boost::shared_ptr<Playlist> pl = tr->playlist ();
6180 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6183 positions.push_back (result);
6196 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6197 (*r)->region()->get_transients (positions);
6201 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6204 AnalysisFeatureList::iterator x;
6206 for (x = positions.begin(); x != positions.end(); ++x) {
6212 if (x != positions.end ()) {
6213 _session->request_locate (*x);
6217 AnalysisFeatureList::reverse_iterator x;
6219 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6225 if (x != positions.rend ()) {
6226 _session->request_locate (*x);
6232 Editor::playhead_forward_to_grid ()
6234 if (!_session) return;
6235 framepos_t pos = playhead_cursor->current_frame;
6236 if (pos < max_framepos - 1) {
6238 snap_to_internal (pos, 1, false);
6239 _session->request_locate (pos);
6245 Editor::playhead_backward_to_grid ()
6247 if (!_session) return;
6248 framepos_t pos = playhead_cursor->current_frame;
6251 snap_to_internal (pos, -1, false);
6252 _session->request_locate (pos);
6257 Editor::set_track_height (Height h)
6259 TrackSelection& ts (selection->tracks);
6261 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6262 (*x)->set_height_enum (h);
6267 Editor::toggle_tracks_active ()
6269 TrackSelection& ts (selection->tracks);
6271 bool target = false;
6277 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6278 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6282 target = !rtv->_route->active();
6285 rtv->_route->set_active (target, this);
6291 Editor::remove_tracks ()
6293 TrackSelection& ts (selection->tracks);
6299 vector<string> choices;
6303 const char* trackstr;
6305 vector<boost::shared_ptr<Route> > routes;
6306 bool special_bus = false;
6308 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6309 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6311 if (rtv->is_track()) {
6317 routes.push_back (rtv->_route);
6319 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6324 if (special_bus && !Config->get_allow_special_bus_removal()) {
6325 MessageDialog msg (_("That would be bad news ...."),
6329 msg.set_secondary_text (string_compose (_(
6330 "Removing the master or monitor bus is such a bad idea\n\
6331 that %1 is not going to allow it.\n\
6333 If you really want to do this sort of thing\n\
6334 edit your ardour.rc file to set the\n\
6335 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6342 if (ntracks + nbusses == 0) {
6347 trackstr = _("tracks");
6349 trackstr = _("track");
6353 busstr = _("busses");
6360 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6361 "(You may also lose the playlists associated with the %2)\n\n"
6362 "This action cannot be undone, and the session file will be overwritten!"),
6363 ntracks, trackstr, nbusses, busstr);
6365 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6366 "(You may also lose the playlists associated with the %2)\n\n"
6367 "This action cannot be undone, and the session file will be overwritten!"),
6370 } else if (nbusses) {
6371 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6372 "This action cannot be undon, and the session file will be overwritten"),
6376 choices.push_back (_("No, do nothing."));
6377 if (ntracks + nbusses > 1) {
6378 choices.push_back (_("Yes, remove them."));
6380 choices.push_back (_("Yes, remove it."));
6385 title = string_compose (_("Remove %1"), trackstr);
6387 title = string_compose (_("Remove %1"), busstr);
6390 Choice prompter (title, prompt, choices);
6392 if (prompter.run () != 1) {
6396 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6397 _session->remove_route (*x);
6402 Editor::do_insert_time ()
6404 if (selection->tracks.empty()) {
6408 InsertTimeDialog d (*this);
6409 int response = d.run ();
6411 if (response != RESPONSE_OK) {
6415 if (d.distance() == 0) {
6419 InsertTimeOption opt = d.intersected_region_action ();
6422 get_preferred_edit_position(),
6428 d.move_glued_markers(),
6429 d.move_locked_markers(),
6435 Editor::insert_time (
6436 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6437 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6440 bool commit = false;
6442 if (Config->get_edit_mode() == Lock) {
6446 begin_reversible_command (_("insert time"));
6448 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6450 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6454 /* don't operate on any playlist more than once, which could
6455 * happen if "all playlists" is enabled, but there is more
6456 * than 1 track using playlists "from" a given track.
6459 set<boost::shared_ptr<Playlist> > pl;
6461 if (all_playlists) {
6462 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6464 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6465 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6470 if ((*x)->playlist ()) {
6471 pl.insert ((*x)->playlist ());
6475 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6477 (*i)->clear_changes ();
6478 (*i)->clear_owned_changes ();
6480 if (opt == SplitIntersected) {
6484 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6486 vector<Command*> cmds;
6488 _session->add_commands (cmds);
6490 _session->add_command (new StatefulDiffCommand (*i));
6495 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6497 rtav->route ()->shift (pos, frames);
6505 XMLNode& before (_session->locations()->get_state());
6506 Locations::LocationList copy (_session->locations()->list());
6508 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6510 Locations::LocationList::const_iterator tmp;
6512 bool const was_locked = (*i)->locked ();
6513 if (locked_markers_too) {
6517 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6519 if ((*i)->start() >= pos) {
6520 (*i)->set_start ((*i)->start() + frames);
6521 if (!(*i)->is_mark()) {
6522 (*i)->set_end ((*i)->end() + frames);
6535 XMLNode& after (_session->locations()->get_state());
6536 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6541 _session->tempo_map().insert_time (pos, frames);
6545 commit_reversible_command ();
6550 Editor::fit_selected_tracks ()
6552 if (!selection->tracks.empty()) {
6553 fit_tracks (selection->tracks);
6557 /* no selected tracks - use tracks with selected regions */
6559 if (!selection->regions.empty()) {
6560 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6561 tvl.push_back (&(*r)->get_time_axis_view ());
6567 } else if (internal_editing()) {
6568 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6571 if (entered_track) {
6572 tvl.push_back (entered_track);
6580 Editor::fit_tracks (TrackViewList & tracks)
6582 if (tracks.empty()) {
6586 uint32_t child_heights = 0;
6587 int visible_tracks = 0;
6589 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6591 if (!(*t)->marked_for_display()) {
6595 child_heights += (*t)->effective_height() - (*t)->current_height();
6599 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6600 double first_y_pos = DBL_MAX;
6602 if (h < TimeAxisView::preset_height (HeightSmall)) {
6603 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6604 /* too small to be displayed */
6608 undo_visual_stack.push_back (current_visual_state (true));
6609 no_save_visual = true;
6611 /* build a list of all tracks, including children */
6614 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6616 TimeAxisView::Children c = (*i)->get_child_list ();
6617 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6618 all.push_back (j->get());
6622 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6624 bool prev_was_selected = false;
6625 bool is_selected = tracks.contains (all.front());
6626 bool next_is_selected;
6628 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6630 TrackViewList::iterator next;
6635 if (next != all.end()) {
6636 next_is_selected = tracks.contains (*next);
6638 next_is_selected = false;
6641 if ((*t)->marked_for_display ()) {
6643 (*t)->set_height (h);
6644 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6646 if (prev_was_selected && next_is_selected) {
6647 hide_track_in_display (*t);
6652 prev_was_selected = is_selected;
6653 is_selected = next_is_selected;
6657 set the controls_layout height now, because waiting for its size
6658 request signal handler will cause the vertical adjustment setting to fail
6661 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6662 vertical_adjustment.set_value (first_y_pos);
6664 redo_visual_stack.push_back (current_visual_state (true));
6668 Editor::save_visual_state (uint32_t n)
6670 while (visual_states.size() <= n) {
6671 visual_states.push_back (0);
6674 if (visual_states[n] != 0) {
6675 delete visual_states[n];
6678 visual_states[n] = current_visual_state (true);
6683 Editor::goto_visual_state (uint32_t n)
6685 if (visual_states.size() <= n) {
6689 if (visual_states[n] == 0) {
6693 use_visual_state (*visual_states[n]);
6697 Editor::start_visual_state_op (uint32_t n)
6699 save_visual_state (n);
6701 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6703 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6704 pup->set_text (buf);
6709 Editor::cancel_visual_state_op (uint32_t n)
6711 goto_visual_state (n);
6715 Editor::toggle_region_mute ()
6717 if (_ignore_region_action) {
6721 RegionSelection rs = get_regions_from_selection_and_entered ();
6727 if (rs.size() > 1) {
6728 begin_reversible_command (_("mute regions"));
6730 begin_reversible_command (_("mute region"));
6733 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6735 (*i)->region()->playlist()->clear_changes ();
6736 (*i)->region()->set_muted (!(*i)->region()->muted ());
6737 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6741 commit_reversible_command ();
6745 Editor::combine_regions ()
6747 /* foreach track with selected regions, take all selected regions
6748 and join them into a new region containing the subregions (as a
6752 typedef set<RouteTimeAxisView*> RTVS;
6755 if (selection->regions.empty()) {
6759 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6760 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6763 tracks.insert (rtv);
6767 begin_reversible_command (_("combine regions"));
6769 vector<RegionView*> new_selection;
6771 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6774 if ((rv = (*i)->combine_regions ()) != 0) {
6775 new_selection.push_back (rv);
6779 selection->clear_regions ();
6780 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6781 selection->add (*i);
6784 commit_reversible_command ();
6788 Editor::uncombine_regions ()
6790 typedef set<RouteTimeAxisView*> RTVS;
6793 if (selection->regions.empty()) {
6797 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6798 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6801 tracks.insert (rtv);
6805 begin_reversible_command (_("uncombine regions"));
6807 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6808 (*i)->uncombine_regions ();
6811 commit_reversible_command ();