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.get_buffers ();
3397 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3398 current_interthread_info->done = true;
3399 current_interthread_info->process_thread.drop_buffers();
3404 Editor::freeze_route ()
3410 /* stop transport before we start. this is important */
3412 _session->request_transport_speed (0.0);
3414 /* wait for just a little while, because the above call is asynchronous */
3418 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3422 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3424 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3425 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3427 d.set_title (_("Cannot freeze"));
3432 if (clicked_routeview->track()->has_external_redirects()) {
3433 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"
3434 "Freezing will only process the signal as far as the first send/insert/return."),
3435 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3437 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3438 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3439 d.set_title (_("Freeze Limits"));
3441 int response = d.run ();
3444 case Gtk::RESPONSE_CANCEL:
3451 InterThreadInfo itt;
3452 current_interthread_info = &itt;
3454 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3456 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3458 set_canvas_cursor (_cursors->wait);
3460 while (!itt.done && !itt.cancel) {
3461 gtk_main_iteration ();
3464 current_interthread_info = 0;
3465 set_canvas_cursor (current_canvas_cursor);
3469 Editor::bounce_range_selection (bool replace, bool enable_processing)
3471 if (selection->time.empty()) {
3475 TrackSelection views = selection->tracks;
3477 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3479 if (enable_processing) {
3481 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3483 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3485 _("You can't perform this operation because the processing of the signal "
3486 "will cause one or more of the tracks will end up with a region with more channels than this track has inputs.\n\n"
3487 "You can do this without processing, which is a different operation.")
3489 d.set_title (_("Cannot bounce"));
3496 framepos_t start = selection->time[clicked_selection].start;
3497 framepos_t end = selection->time[clicked_selection].end;
3498 framepos_t cnt = end - start + 1;
3500 begin_reversible_command (_("bounce range"));
3502 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3504 RouteTimeAxisView* rtv;
3506 if ((rtv = dynamic_cast<RouteTimeAxisView*> (*i)) == 0) {
3510 boost::shared_ptr<Playlist> playlist;
3512 if ((playlist = rtv->playlist()) == 0) {
3516 InterThreadInfo itt;
3518 playlist->clear_changes ();
3519 playlist->clear_owned_changes ();
3521 boost::shared_ptr<Region> r;
3523 if (enable_processing) {
3524 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3526 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
3534 list<AudioRange> ranges;
3535 ranges.push_back (AudioRange (start, start+cnt, 0));
3536 playlist->cut (ranges); // discard result
3537 playlist->add_region (r, start);
3540 vector<Command*> cmds;
3541 playlist->rdiff (cmds);
3542 _session->add_commands (cmds);
3544 _session->add_command (new StatefulDiffCommand (playlist));
3547 commit_reversible_command ();
3550 /** Delete selected regions, automation points or a time range */
3557 /** Cut selected regions, automation points or a time range */
3564 /** Copy selected regions, automation points or a time range */
3572 /** @return true if a Cut, Copy or Clear is possible */
3574 Editor::can_cut_copy () const
3576 switch (current_mouse_mode()) {
3579 if (!selection->regions.empty() || !selection->points.empty()) {
3585 if (!selection->time.empty()) {
3598 /** Cut, copy or clear selected regions, automation points or a time range.
3599 * @param op Operation (Cut, Copy or Clear)
3602 Editor::cut_copy (CutCopyOp op)
3604 /* only cancel selection if cut/copy is successful.*/
3610 opname = _("delete");
3619 opname = _("clear");
3623 /* if we're deleting something, and the mouse is still pressed,
3624 the thing we started a drag for will be gone when we release
3625 the mouse button(s). avoid this. see part 2 at the end of
3629 if (op == Delete || op == Cut || op == Clear) {
3630 if (_drags->active ()) {
3635 cut_buffer->clear ();
3637 if (entered_marker) {
3639 /* cut/delete op while pointing at a marker */
3642 Location* loc = find_location_from_marker (entered_marker, ignored);
3644 if (_session && loc) {
3645 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
3652 if (internal_editing()) {
3654 switch (current_mouse_mode()) {
3667 /* we only want to cut regions if some are selected */
3669 if (doing_object_stuff()) {
3670 rs = get_regions_from_selection ();
3671 if (!rs.empty() || !selection->points.empty()) {
3673 begin_reversible_command (opname + _(" objects"));
3676 cut_copy_regions (op, rs);
3678 if (op == Cut || op == Delete) {
3679 selection->clear_regions ();
3683 if (!selection->points.empty()) {
3684 cut_copy_points (op);
3686 if (op == Cut || op == Delete) {
3687 selection->clear_points ();
3690 commit_reversible_command ();
3693 if (!selection->time.empty() && (_join_object_range_state == JOIN_OBJECT_RANGE_NONE)) {
3694 /* don't cause suprises */
3699 if (doing_range_stuff()) {
3700 if (selection->time.empty()) {
3701 framepos_t start, end;
3702 if (!get_edit_op_range (start, end)) {
3705 selection->set (start, end);
3708 begin_reversible_command (opname + _(" range"));
3709 cut_copy_ranges (op);
3710 commit_reversible_command ();
3712 if (op == Cut || op == Delete) {
3713 selection->clear_time ();
3719 if (op == Delete || op == Cut || op == Clear) {
3724 struct AutomationRecord {
3725 AutomationRecord () : state (0) {}
3726 AutomationRecord (XMLNode* s) : state (s) {}
3728 XMLNode* state; ///< state before any operation
3729 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
3732 /** Cut, copy or clear selected automation points.
3733 * @param op Operation (Cut, Copy or Clear)
3736 Editor::cut_copy_points (CutCopyOp op)
3738 if (selection->points.empty ()) {
3742 /* XXX: not ideal, as there may be more than one track involved in the point selection */
3743 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
3745 /* Keep a record of the AutomationLists that we end up using in this operation */
3746 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
3749 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
3750 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3751 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3752 if (lists.find (al) == lists.end ()) {
3753 /* We haven't seen this list yet, so make a record for it. This includes
3754 taking a copy of its current state, in case this is needed for undo later.
3756 lists[al] = AutomationRecord (&al->get_state ());
3760 if (op == Cut || op == Copy) {
3761 /* This operation will involve putting things in the cut buffer, so create an empty
3762 ControlList for each of our source lists to put the cut buffer data in.
3764 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3765 i->second.copy = i->first->create (i->first->parameter ());
3768 /* Add all selected points to the relevant copy ControlLists */
3769 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3770 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3771 AutomationList::const_iterator j = (*i)->model ();
3772 lists[al].copy->add ((*j)->when, (*j)->value);
3775 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3776 /* Correct this copy list so that it starts at time 0 */
3777 double const start = i->second.copy->front()->when;
3778 for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) {
3779 (*j)->when -= start;
3782 /* And add it to the cut buffer */
3783 cut_buffer->add (i->second.copy);
3787 if (op == Delete || op == Cut) {
3788 /* This operation needs to remove things from the main AutomationList, so do that now */
3790 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3791 i->first->freeze ();
3794 /* Remove each selected point from its AutomationList */
3795 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
3796 boost::shared_ptr<AutomationList> al = (*i)->line().the_list();
3797 al->erase ((*i)->model ());
3800 /* Thaw the lists and add undo records for them */
3801 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
3802 boost::shared_ptr<AutomationList> al = i->first;
3804 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
3809 /** Cut, copy or clear selected automation points.
3810 * @param op Operation (Cut, Copy or Clear)
3813 Editor::cut_copy_midi (CutCopyOp op)
3815 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
3816 MidiRegionView* mrv = *i;
3817 mrv->cut_copy_clear (op);
3823 struct lt_playlist {
3824 bool operator () (const PlaylistState& a, const PlaylistState& b) {
3825 return a.playlist < b.playlist;
3829 struct PlaylistMapping {
3831 boost::shared_ptr<Playlist> pl;
3833 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
3836 /** Remove `clicked_regionview' */
3838 Editor::remove_clicked_region ()
3840 if (clicked_routeview == 0 || clicked_regionview == 0) {
3844 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
3846 begin_reversible_command (_("remove region"));
3847 playlist->clear_changes ();
3848 playlist->clear_owned_changes ();
3849 playlist->remove_region (clicked_regionview->region());
3851 /* We might have removed regions, which alters other regions' layering_index,
3852 so we need to do a recursive diff here.
3854 vector<Command*> cmds;
3855 playlist->rdiff (cmds);
3856 _session->add_commands (cmds);
3858 _session->add_command(new StatefulDiffCommand (playlist));
3859 commit_reversible_command ();
3863 /** Remove the selected regions */
3865 Editor::remove_selected_regions ()
3867 RegionSelection rs = get_regions_from_selection_and_entered ();
3869 if (!_session || rs.empty()) {
3873 begin_reversible_command (_("remove region"));
3875 list<boost::shared_ptr<Region> > regions_to_remove;
3877 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3878 // we can't just remove the region(s) in this loop because
3879 // this removes them from the RegionSelection, and they thus
3880 // disappear from underneath the iterator, and the ++i above
3881 // SEGVs in a puzzling fashion.
3883 // so, first iterate over the regions to be removed from rs and
3884 // add them to the regions_to_remove list, and then
3885 // iterate over the list to actually remove them.
3887 regions_to_remove.push_back ((*i)->region());
3890 vector<boost::shared_ptr<Playlist> > playlists;
3892 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3894 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3897 // is this check necessary?
3901 /* get_regions_from_selection_and_entered() guarantees that
3902 the playlists involved are unique, so there is no need
3906 playlists.push_back (playlist);
3908 playlist->clear_changes ();
3909 playlist->clear_owned_changes ();
3910 playlist->freeze ();
3911 playlist->remove_region (*rl);
3914 vector<boost::shared_ptr<Playlist> >::iterator pl;
3916 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3919 /* We might have removed regions, which alters other regions' layering_index,
3920 so we need to do a recursive diff here.
3922 vector<Command*> cmds;
3923 (*pl)->rdiff (cmds);
3924 _session->add_commands (cmds);
3926 _session->add_command(new StatefulDiffCommand (*pl));
3929 commit_reversible_command ();
3932 /** Cut, copy or clear selected regions.
3933 * @param op Operation (Cut, Copy or Clear)
3936 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
3938 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
3939 a map when we want ordered access to both elements. i think.
3942 vector<PlaylistMapping> pmap;
3944 framepos_t first_position = max_framepos;
3946 typedef set<boost::shared_ptr<Playlist> > FreezeList;
3947 FreezeList freezelist;
3949 /* get ordering correct before we cut/copy */
3951 rs.sort_by_position_and_track ();
3953 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3955 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
3957 if (op == Cut || op == Clear || op == Delete) {
3958 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3961 FreezeList::iterator fl;
3963 // only take state if this is a new playlist.
3964 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
3970 if (fl == freezelist.end()) {
3971 pl->clear_changes();
3972 pl->clear_owned_changes ();
3974 freezelist.insert (pl);
3979 TimeAxisView* tv = &(*x)->get_time_axis_view();
3980 vector<PlaylistMapping>::iterator z;
3982 for (z = pmap.begin(); z != pmap.end(); ++z) {
3983 if ((*z).tv == tv) {
3988 if (z == pmap.end()) {
3989 pmap.push_back (PlaylistMapping (tv));
3993 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
3995 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
3998 /* region not yet associated with a playlist (e.g. unfinished
4005 TimeAxisView& tv = (*x)->get_time_axis_view();
4006 boost::shared_ptr<Playlist> npl;
4007 RegionSelection::iterator tmp;
4014 vector<PlaylistMapping>::iterator z;
4016 for (z = pmap.begin(); z != pmap.end(); ++z) {
4017 if ((*z).tv == &tv) {
4022 assert (z != pmap.end());
4025 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4033 boost::shared_ptr<Region> r = (*x)->region();
4034 boost::shared_ptr<Region> _xx;
4040 pl->remove_region (r);
4044 _xx = RegionFactory::create (r);
4045 npl->add_region (_xx, r->position() - first_position);
4046 pl->remove_region (r);
4050 /* copy region before adding, so we're not putting same object into two different playlists */
4051 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4055 pl->remove_region (r);
4064 list<boost::shared_ptr<Playlist> > foo;
4066 /* the pmap is in the same order as the tracks in which selected regions occured */
4068 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4071 foo.push_back ((*i).pl);
4076 cut_buffer->set (foo);
4080 _last_cut_copy_source_track = 0;
4082 _last_cut_copy_source_track = pmap.front().tv;
4086 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4089 /* We might have removed regions, which alters other regions' layering_index,
4090 so we need to do a recursive diff here.
4092 vector<Command*> cmds;
4093 (*pl)->rdiff (cmds);
4094 _session->add_commands (cmds);
4096 _session->add_command (new StatefulDiffCommand (*pl));
4101 Editor::cut_copy_ranges (CutCopyOp op)
4103 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4105 /* Sort the track selection now, so that it if is used, the playlists
4106 selected by the calls below to cut_copy_clear are in the order that
4107 their tracks appear in the editor. This makes things like paste
4108 of ranges work properly.
4111 sort_track_selection (ts);
4114 if (!entered_track) {
4117 ts.push_back (entered_track);
4120 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4121 (*i)->cut_copy_clear (*selection, op);
4126 Editor::paste (float times, bool from_context)
4128 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4130 paste_internal (get_preferred_edit_position (false, from_context), times);
4134 Editor::mouse_paste ()
4139 if (!mouse_frame (where, ignored)) {
4144 paste_internal (where, 1);
4148 Editor::paste_internal (framepos_t position, float times)
4150 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4152 if (internal_editing()) {
4153 if (cut_buffer->midi_notes.empty()) {
4157 if (cut_buffer->empty()) {
4162 if (position == max_framepos) {
4163 position = get_preferred_edit_position();
4164 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4168 TrackViewList::iterator i;
4171 /* get everything in the correct order */
4173 if (_edit_point == Editing::EditAtMouse && entered_track) {
4174 /* With the mouse edit point, paste onto the track under the mouse */
4175 ts.push_back (entered_track);
4176 } else if (!selection->tracks.empty()) {
4177 /* Otherwise, if there are some selected tracks, paste to them */
4178 ts = selection->tracks.filter_to_unique_playlists ();
4179 sort_track_selection (ts);
4180 } else if (_last_cut_copy_source_track) {
4181 /* Otherwise paste to the track that the cut/copy came from;
4182 see discussion in mantis #3333.
4184 ts.push_back (_last_cut_copy_source_track);
4187 if (internal_editing ()) {
4189 /* undo/redo is handled by individual tracks/regions */
4191 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4194 RegionSelection::iterator r;
4195 MidiNoteSelection::iterator cb;
4197 get_regions_at (rs, position, ts);
4199 for (cb = cut_buffer->midi_notes.begin(), r = rs.begin();
4200 cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
4201 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
4203 mrv->paste (position, times, **cb);
4211 /* we do redo (do you do voodoo?) */
4213 begin_reversible_command (Operations::paste);
4215 for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) {
4216 (*i)->paste (position, times, *cut_buffer, nth);
4219 commit_reversible_command ();
4224 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4226 boost::shared_ptr<Playlist> playlist;
4227 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4228 RegionSelection foo;
4230 framepos_t const start_frame = regions.start ();
4231 framepos_t const end_frame = regions.end_frame ();
4233 begin_reversible_command (Operations::duplicate_region);
4235 selection->clear_regions ();
4237 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4239 boost::shared_ptr<Region> r ((*i)->region());
4241 TimeAxisView& tv = (*i)->get_time_axis_view();
4242 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4243 latest_regionviews.clear ();
4244 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4246 playlist = (*i)->region()->playlist();
4247 playlist->clear_changes ();
4248 playlist->duplicate (r, end_frame + (r->first_frame() - start_frame), times);
4249 _session->add_command(new StatefulDiffCommand (playlist));
4253 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4256 commit_reversible_command ();
4259 selection->set (foo);
4264 Editor::duplicate_selection (float times)
4266 if (selection->time.empty() || selection->tracks.empty()) {
4270 boost::shared_ptr<Playlist> playlist;
4271 vector<boost::shared_ptr<Region> > new_regions;
4272 vector<boost::shared_ptr<Region> >::iterator ri;
4274 create_region_from_selection (new_regions);
4276 if (new_regions.empty()) {
4280 begin_reversible_command (_("duplicate selection"));
4282 ri = new_regions.begin();
4284 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4286 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4287 if ((playlist = (*i)->playlist()) == 0) {
4290 playlist->clear_changes ();
4291 playlist->duplicate (*ri, selection->time[clicked_selection].end, times);
4292 _session->add_command (new StatefulDiffCommand (playlist));
4295 if (ri == new_regions.end()) {
4300 commit_reversible_command ();
4303 /** Reset all selected points to the relevant default value */
4305 Editor::reset_point_selection ()
4307 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4308 ARDOUR::AutomationList::iterator j = (*i)->model ();
4309 (*j)->value = (*i)->line().the_list()->default_value ();
4314 Editor::center_playhead ()
4316 float page = _canvas_width * frames_per_unit;
4317 center_screen_internal (playhead_cursor->current_frame, page);
4321 Editor::center_edit_point ()
4323 float page = _canvas_width * frames_per_unit;
4324 center_screen_internal (get_preferred_edit_position(), page);
4327 /** Caller must begin and commit a reversible command */
4329 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4331 playlist->clear_changes ();
4333 _session->add_command (new StatefulDiffCommand (playlist));
4337 Editor::nudge_track (bool use_edit, bool forwards)
4339 boost::shared_ptr<Playlist> playlist;
4340 framepos_t distance;
4341 framepos_t next_distance;
4345 start = get_preferred_edit_position();
4350 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4354 if (selection->tracks.empty()) {
4358 begin_reversible_command (_("nudge track"));
4360 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4362 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4364 if ((playlist = (*i)->playlist()) == 0) {
4368 playlist->clear_changes ();
4369 playlist->clear_owned_changes ();
4371 playlist->nudge_after (start, distance, forwards);
4373 vector<Command*> cmds;
4375 playlist->rdiff (cmds);
4376 _session->add_commands (cmds);
4378 _session->add_command (new StatefulDiffCommand (playlist));
4381 commit_reversible_command ();
4385 Editor::remove_last_capture ()
4387 vector<string> choices;
4394 if (Config->get_verify_remove_last_capture()) {
4395 prompt = _("Do you really want to destroy the last capture?"
4396 "\n(This is destructive and cannot be undone)");
4398 choices.push_back (_("No, do nothing."));
4399 choices.push_back (_("Yes, destroy it."));
4401 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
4403 if (prompter.run () == 1) {
4404 _session->remove_last_capture ();
4405 _regions->redisplay ();
4409 _session->remove_last_capture();
4410 _regions->redisplay ();
4415 Editor::normalize_region ()
4421 RegionSelection rs = get_regions_from_selection_and_entered ();
4427 NormalizeDialog dialog (rs.size() > 1);
4429 if (dialog.run () == RESPONSE_CANCEL) {
4433 set_canvas_cursor (_cursors->wait);
4436 /* XXX: should really only count audio regions here */
4437 int const regions = rs.size ();
4439 /* Make a list of the selected audio regions' maximum amplitudes, and also
4440 obtain the maximum amplitude of them all.
4442 list<double> max_amps;
4444 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
4445 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
4447 dialog.descend (1.0 / regions);
4448 double const a = arv->audio_region()->maximum_amplitude (&dialog);
4451 /* the user cancelled the operation */
4452 set_canvas_cursor (current_canvas_cursor);
4456 max_amps.push_back (a);
4457 max_amp = max (max_amp, a);
4462 begin_reversible_command (_("normalize"));
4464 list<double>::const_iterator a = max_amps.begin ();
4466 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4467 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
4472 arv->region()->clear_changes ();
4474 double const amp = dialog.normalize_individually() ? *a : max_amp;
4476 arv->audio_region()->normalize (amp, dialog.target ());
4477 _session->add_command (new StatefulDiffCommand (arv->region()));
4482 commit_reversible_command ();
4483 set_canvas_cursor (current_canvas_cursor);
4488 Editor::reset_region_scale_amplitude ()
4494 RegionSelection rs = get_regions_from_selection_and_entered ();
4500 begin_reversible_command ("reset gain");
4502 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4503 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4506 arv->region()->clear_changes ();
4507 arv->audio_region()->set_scale_amplitude (1.0f);
4508 _session->add_command (new StatefulDiffCommand (arv->region()));
4511 commit_reversible_command ();
4515 Editor::adjust_region_gain (bool up)
4517 RegionSelection rs = get_regions_from_selection_and_entered ();
4519 if (!_session || rs.empty()) {
4523 begin_reversible_command ("adjust region gain");
4525 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4526 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4531 arv->region()->clear_changes ();
4533 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
4541 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
4542 _session->add_command (new StatefulDiffCommand (arv->region()));
4545 commit_reversible_command ();
4550 Editor::reverse_region ()
4556 Reverse rev (*_session);
4557 apply_filter (rev, _("reverse regions"));
4561 Editor::strip_region_silence ()
4567 RegionSelection rs = get_regions_from_selection_and_entered ();
4573 std::list<RegionView*> audio_only;
4575 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4576 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
4578 audio_only.push_back (arv);
4582 StripSilenceDialog d (_session, audio_only);
4583 int const r = d.run ();
4587 if (r == Gtk::RESPONSE_OK) {
4588 ARDOUR::AudioIntervalMap silences;
4589 d.silences (silences);
4590 StripSilence s (*_session, silences, d.fade_length());
4591 apply_filter (s, _("strip silence"), &d);
4596 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
4598 Evoral::Sequence<Evoral::MusicalTime>::Notes selected;
4599 mrv.selection_as_notelist (selected, true);
4601 vector<Evoral::Sequence<Evoral::MusicalTime>::Notes> v;
4602 v.push_back (selected);
4604 framepos_t pos_frames = mrv.midi_region()->position();
4605 double pos_beats = _session->tempo_map().framewalk_to_beats(0, pos_frames);
4607 return op (mrv.midi_region()->model(), pos_beats, v);
4611 Editor::apply_midi_note_edit_op (MidiOperator& op)
4615 RegionSelection rs = get_regions_from_selection_and_entered ();
4621 begin_reversible_command (op.name ());
4623 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4624 RegionSelection::iterator tmp = r;
4627 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4630 cmd = apply_midi_note_edit_op_to_region (op, *mrv);
4633 _session->add_command (cmd);
4640 commit_reversible_command ();
4644 Editor::fork_region ()
4646 RegionSelection rs = get_regions_from_selection_and_entered ();
4652 begin_reversible_command (_("Fork Region(s)"));
4654 set_canvas_cursor (_cursors->wait);
4657 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4658 RegionSelection::iterator tmp = r;
4661 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
4664 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
4665 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone ();
4667 playlist->clear_changes ();
4668 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
4669 _session->add_command(new StatefulDiffCommand (playlist));
4675 commit_reversible_command ();
4677 set_canvas_cursor (current_canvas_cursor);
4681 Editor::quantize_region ()
4683 int selected_midi_region_cnt = 0;
4689 RegionSelection rs = get_regions_from_selection_and_entered ();
4695 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
4696 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
4698 selected_midi_region_cnt++;
4702 if (selected_midi_region_cnt == 0) {
4706 QuantizeDialog* qd = new QuantizeDialog (*this);
4709 const int r = qd->run ();
4712 if (r == Gtk::RESPONSE_OK) {
4713 Quantize quant (*_session, qd->snap_start(), qd->snap_end(),
4714 qd->start_grid_size(), qd->end_grid_size(),
4715 qd->strength(), qd->swing(), qd->threshold());
4717 apply_midi_note_edit_op (quant);
4722 Editor::insert_patch_change (bool from_context)
4724 RegionSelection rs = get_regions_from_selection_and_entered ();
4730 const framepos_t p = get_preferred_edit_position (false, from_context);
4732 cerr << "Got " << rs.size() << " regions to add patch change to\n";
4734 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
4735 there may be more than one, but the PatchChangeDialog can only offer
4736 one set of patch menus.
4738 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
4740 Evoral::PatchChange<Evoral::MusicalTime> empty (0, 0, 0, 0);
4741 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
4743 if (d.run() == RESPONSE_CANCEL) {
4747 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4748 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
4750 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
4751 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
4758 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
4760 RegionSelection rs = get_regions_from_selection_and_entered ();
4766 begin_reversible_command (command);
4768 set_canvas_cursor (_cursors->wait);
4772 int const N = rs.size ();
4774 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
4775 RegionSelection::iterator tmp = r;
4778 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
4780 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
4783 progress->descend (1.0 / N);
4786 if (arv->audio_region()->apply (filter, progress) == 0) {
4788 playlist->clear_changes ();
4789 playlist->clear_owned_changes ();
4791 if (filter.results.empty ()) {
4793 /* no regions returned; remove the old one */
4794 playlist->remove_region (arv->region ());
4798 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
4800 /* first region replaces the old one */
4801 playlist->replace_region (arv->region(), *res, (*res)->position());
4805 while (res != filter.results.end()) {
4806 playlist->add_region (*res, (*res)->position());
4812 /* We might have removed regions, which alters other regions' layering_index,
4813 so we need to do a recursive diff here.
4815 vector<Command*> cmds;
4816 playlist->rdiff (cmds);
4817 _session->add_commands (cmds);
4819 _session->add_command(new StatefulDiffCommand (playlist));
4825 progress->ascend ();
4833 commit_reversible_command ();
4836 set_canvas_cursor (current_canvas_cursor);
4840 Editor::external_edit_region ()
4846 Editor::reset_region_gain_envelopes ()
4848 RegionSelection rs = get_regions_from_selection_and_entered ();
4850 if (!_session || rs.empty()) {
4854 _session->begin_reversible_command (_("reset region gain"));
4856 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4857 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4859 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
4860 XMLNode& before (alist->get_state());
4862 arv->audio_region()->set_default_envelope ();
4863 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
4867 _session->commit_reversible_command ();
4871 Editor::set_region_gain_visibility (RegionView* rv)
4873 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
4875 arv->update_envelope_visibility();
4880 Editor::set_gain_envelope_visibility ()
4886 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4887 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
4889 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
4895 Editor::toggle_gain_envelope_active ()
4897 if (_ignore_region_action) {
4901 RegionSelection rs = get_regions_from_selection_and_entered ();
4903 if (!_session || rs.empty()) {
4907 _session->begin_reversible_command (_("region gain envelope active"));
4909 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4910 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
4912 arv->region()->clear_changes ();
4913 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
4914 _session->add_command (new StatefulDiffCommand (arv->region()));
4918 _session->commit_reversible_command ();
4922 Editor::toggle_region_lock ()
4924 if (_ignore_region_action) {
4928 RegionSelection rs = get_regions_from_selection_and_entered ();
4930 if (!_session || rs.empty()) {
4934 _session->begin_reversible_command (_("toggle region lock"));
4936 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4937 (*i)->region()->clear_changes ();
4938 (*i)->region()->set_locked (!(*i)->region()->locked());
4939 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4942 _session->commit_reversible_command ();
4946 Editor::toggle_region_lock_style ()
4948 if (_ignore_region_action) {
4952 RegionSelection rs = get_regions_from_selection_and_entered ();
4954 if (!_session || rs.empty()) {
4958 _session->begin_reversible_command (_("region lock style"));
4960 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4961 (*i)->region()->clear_changes ();
4962 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
4963 (*i)->region()->set_position_lock_style (ns);
4964 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4967 _session->commit_reversible_command ();
4971 Editor::toggle_opaque_region ()
4973 if (_ignore_region_action) {
4977 RegionSelection rs = get_regions_from_selection_and_entered ();
4979 if (!_session || rs.empty()) {
4983 _session->begin_reversible_command (_("change region opacity"));
4985 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4986 (*i)->region()->clear_changes ();
4987 (*i)->region()->set_opaque (!(*i)->region()->opaque());
4988 _session->add_command (new StatefulDiffCommand ((*i)->region()));
4991 _session->commit_reversible_command ();
4995 Editor::toggle_record_enable ()
4997 bool new_state = false;
4999 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5000 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5003 if (!rtav->is_track())
5007 new_state = !rtav->track()->record_enabled();
5011 rtav->track()->set_record_enabled (new_state, this);
5016 Editor::toggle_solo ()
5018 bool new_state = false;
5020 boost::shared_ptr<RouteList> rl (new RouteList);
5022 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5023 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5030 new_state = !rtav->route()->soloed ();
5034 rl->push_back (rtav->route());
5037 _session->set_solo (rl, new_state, Session::rt_cleanup, true);
5041 Editor::toggle_mute ()
5043 bool new_state = false;
5045 boost::shared_ptr<RouteList> rl (new RouteList);
5047 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5048 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5055 new_state = !rtav->route()->muted();
5059 rl->push_back (rtav->route());
5062 _session->set_mute (rl, new_state, Session::rt_cleanup, true);
5066 Editor::toggle_solo_isolate ()
5071 Editor::set_fade_length (bool in)
5073 RegionSelection rs = get_regions_from_selection_and_entered ();
5079 /* we need a region to measure the offset from the start */
5081 RegionView* rv = rs.front ();
5083 framepos_t pos = get_preferred_edit_position();
5087 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5088 /* edit point is outside the relevant region */
5093 if (pos <= rv->region()->position()) {
5097 len = pos - rv->region()->position();
5098 cmd = _("set fade in length");
5100 if (pos >= rv->region()->last_frame()) {
5104 len = rv->region()->last_frame() - pos;
5105 cmd = _("set fade out length");
5108 begin_reversible_command (cmd);
5110 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5111 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5117 boost::shared_ptr<AutomationList> alist;
5119 alist = tmp->audio_region()->fade_in();
5121 alist = tmp->audio_region()->fade_out();
5124 XMLNode &before = alist->get_state();
5127 tmp->audio_region()->set_fade_in_length (len);
5128 tmp->audio_region()->set_fade_in_active (true);
5130 tmp->audio_region()->set_fade_out_length (len);
5131 tmp->audio_region()->set_fade_out_active (true);
5134 XMLNode &after = alist->get_state();
5135 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5138 commit_reversible_command ();
5142 Editor::set_fade_in_shape (FadeShape shape)
5144 RegionSelection rs = get_regions_from_selection_and_entered ();
5150 begin_reversible_command (_("set fade in shape"));
5152 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5153 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5159 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5160 XMLNode &before = alist->get_state();
5162 tmp->audio_region()->set_fade_in_shape (shape);
5164 XMLNode &after = alist->get_state();
5165 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5168 commit_reversible_command ();
5173 Editor::set_fade_out_shape (FadeShape shape)
5175 RegionSelection rs = get_regions_from_selection_and_entered ();
5181 begin_reversible_command (_("set fade out shape"));
5183 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5184 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5190 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5191 XMLNode &before = alist->get_state();
5193 tmp->audio_region()->set_fade_out_shape (shape);
5195 XMLNode &after = alist->get_state();
5196 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5199 commit_reversible_command ();
5203 Editor::set_fade_in_active (bool yn)
5205 RegionSelection rs = get_regions_from_selection_and_entered ();
5211 begin_reversible_command (_("set fade in active"));
5213 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5214 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5221 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5223 ar->clear_changes ();
5224 ar->set_fade_in_active (yn);
5225 _session->add_command (new StatefulDiffCommand (ar));
5228 commit_reversible_command ();
5232 Editor::set_fade_out_active (bool yn)
5234 RegionSelection rs = get_regions_from_selection_and_entered ();
5240 begin_reversible_command (_("set fade out active"));
5242 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5243 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5249 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
5251 ar->clear_changes ();
5252 ar->set_fade_out_active (yn);
5253 _session->add_command(new StatefulDiffCommand (ar));
5256 commit_reversible_command ();
5260 Editor::toggle_region_fades (int dir)
5262 if (_ignore_region_action) {
5266 boost::shared_ptr<AudioRegion> ar;
5269 RegionSelection rs = get_regions_from_selection_and_entered ();
5275 RegionSelection::iterator i;
5276 for (i = rs.begin(); i != rs.end(); ++i) {
5277 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
5279 yn = ar->fade_out_active ();
5281 yn = ar->fade_in_active ();
5287 if (i == rs.end()) {
5291 /* XXX should this undo-able? */
5293 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5294 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
5297 if (dir == 1 || dir == 0) {
5298 ar->set_fade_in_active (!yn);
5301 if (dir == -1 || dir == 0) {
5302 ar->set_fade_out_active (!yn);
5308 /** Update region fade visibility after its configuration has been changed */
5310 Editor::update_region_fade_visibility ()
5312 bool _fade_visibility = _session->config.get_show_region_fades ();
5314 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5315 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5317 if (_fade_visibility) {
5318 v->audio_view()->show_all_fades ();
5320 v->audio_view()->hide_all_fades ();
5327 Editor::set_edit_point ()
5332 if (!mouse_frame (where, ignored)) {
5338 if (selection->markers.empty()) {
5340 mouse_add_new_marker (where);
5345 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
5348 loc->move_to (where);
5354 Editor::set_playhead_cursor ()
5356 if (entered_marker) {
5357 _session->request_locate (entered_marker->position(), _session->transport_rolling());
5362 if (!mouse_frame (where, ignored)) {
5369 _session->request_locate (where, _session->transport_rolling());
5375 Editor::split_region ()
5377 if (((mouse_mode == MouseRange) ||
5378 (mouse_mode != MouseObject && _join_object_range_state == JOIN_OBJECT_RANGE_RANGE)) &&
5379 !selection->time.empty()) {
5380 separate_regions_between (selection->time);
5384 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5386 framepos_t where = get_preferred_edit_position ();
5392 split_regions_at (where, rs);
5395 struct EditorOrderRouteSorter {
5396 bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
5397 /* use of ">" forces the correct sort order */
5398 return a->order_key ("editor") < b->order_key ("editor");
5403 Editor::select_next_route()
5405 if (selection->tracks.empty()) {
5406 selection->set (track_views.front());
5410 TimeAxisView* current = selection->tracks.front();
5414 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5415 if (*i == current) {
5417 if (i != track_views.end()) {
5420 current = (*(track_views.begin()));
5421 //selection->set (*(track_views.begin()));
5426 rui = dynamic_cast<RouteUI *>(current);
5427 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5429 selection->set(current);
5431 ensure_track_visible(current);
5435 Editor::select_prev_route()
5437 if (selection->tracks.empty()) {
5438 selection->set (track_views.front());
5442 TimeAxisView* current = selection->tracks.front();
5446 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
5447 if (*i == current) {
5449 if (i != track_views.rend()) {
5452 current = *(track_views.rbegin());
5457 rui = dynamic_cast<RouteUI *>(current);
5458 } while ( current->hidden() || (rui != NULL && !rui->route()->active()));
5460 selection->set (current);
5462 ensure_track_visible(current);
5466 Editor::ensure_track_visible(TimeAxisView *track)
5468 if (track->hidden())
5471 double const current_view_min_y = vertical_adjustment.get_value();
5472 double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - canvas_timebars_vsize;
5474 double const track_min_y = track->y_position ();
5475 double const track_max_y = track->y_position () + track->effective_height ();
5477 if (track_min_y >= current_view_min_y &&
5478 track_max_y <= current_view_max_y) {
5484 if (track_min_y < current_view_min_y) {
5485 // Track is above the current view
5486 new_value = track_min_y;
5488 // Track is below the current view
5489 new_value = track->y_position () + track->effective_height() + canvas_timebars_vsize - vertical_adjustment.get_page_size();
5492 vertical_adjustment.set_value(new_value);
5496 Editor::set_loop_from_selection (bool play)
5498 if (_session == 0 || selection->time.empty()) {
5502 framepos_t start = selection->time[clicked_selection].start;
5503 framepos_t end = selection->time[clicked_selection].end;
5505 set_loop_range (start, end, _("set loop range from selection"));
5508 _session->request_play_loop (true);
5509 _session->request_locate (start, true);
5514 Editor::set_loop_from_edit_range (bool play)
5516 if (_session == 0) {
5523 if (!get_edit_op_range (start, end)) {
5527 set_loop_range (start, end, _("set loop range from edit range"));
5530 _session->request_play_loop (true);
5531 _session->request_locate (start, true);
5536 Editor::set_loop_from_region (bool play)
5538 framepos_t start = max_framepos;
5541 RegionSelection rs = get_regions_from_selection_and_entered ();
5547 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5548 if ((*i)->region()->position() < start) {
5549 start = (*i)->region()->position();
5551 if ((*i)->region()->last_frame() + 1 > end) {
5552 end = (*i)->region()->last_frame() + 1;
5556 set_loop_range (start, end, _("set loop range from region"));
5559 _session->request_play_loop (true);
5560 _session->request_locate (start, true);
5565 Editor::set_punch_from_selection ()
5567 if (_session == 0 || selection->time.empty()) {
5571 framepos_t start = selection->time[clicked_selection].start;
5572 framepos_t end = selection->time[clicked_selection].end;
5574 set_punch_range (start, end, _("set punch range from selection"));
5578 Editor::set_punch_from_edit_range ()
5580 if (_session == 0) {
5587 if (!get_edit_op_range (start, end)) {
5591 set_punch_range (start, end, _("set punch range from edit range"));
5595 Editor::set_punch_from_region ()
5597 framepos_t start = max_framepos;
5600 RegionSelection rs = get_regions_from_selection_and_entered ();
5606 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5607 if ((*i)->region()->position() < start) {
5608 start = (*i)->region()->position();
5610 if ((*i)->region()->last_frame() + 1 > end) {
5611 end = (*i)->region()->last_frame() + 1;
5615 set_punch_range (start, end, _("set punch range from region"));
5619 Editor::pitch_shift_region ()
5621 RegionSelection rs = get_regions_from_selection_and_entered ();
5623 RegionSelection audio_rs;
5624 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5625 if (dynamic_cast<AudioRegionView*> (*i)) {
5626 audio_rs.push_back (*i);
5630 if (audio_rs.empty()) {
5634 pitch_shift (audio_rs, 1.2);
5638 Editor::transpose_region ()
5640 RegionSelection rs = get_regions_from_selection_and_entered ();
5642 list<MidiRegionView*> midi_region_views;
5643 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5644 MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
5646 midi_region_views.push_back (mrv);
5651 int const r = d.run ();
5652 if (r != RESPONSE_ACCEPT) {
5656 for (list<MidiRegionView*>::iterator i = midi_region_views.begin(); i != midi_region_views.end(); ++i) {
5657 (*i)->midi_region()->transpose (d.semitones ());
5662 Editor::set_tempo_from_region ()
5664 RegionSelection rs = get_regions_from_selection_and_entered ();
5666 if (!_session || rs.empty()) {
5670 RegionView* rv = rs.front();
5672 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
5676 Editor::use_range_as_bar ()
5678 framepos_t start, end;
5679 if (get_edit_op_range (start, end)) {
5680 define_one_bar (start, end);
5685 Editor::define_one_bar (framepos_t start, framepos_t end)
5687 framepos_t length = end - start;
5689 const Meter& m (_session->tempo_map().meter_at (start));
5691 /* length = 1 bar */
5693 /* now we want frames per beat.
5694 we have frames per bar, and beats per bar, so ...
5697 /* XXXX METER MATH */
5699 double frames_per_beat = length / m.divisions_per_bar();
5701 /* beats per minute = */
5703 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
5705 /* now decide whether to:
5707 (a) set global tempo
5708 (b) add a new tempo marker
5712 const TempoSection& t (_session->tempo_map().tempo_section_at (start));
5714 bool do_global = false;
5716 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
5718 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
5719 at the start, or create a new marker
5722 vector<string> options;
5723 options.push_back (_("Cancel"));
5724 options.push_back (_("Add new marker"));
5725 options.push_back (_("Set global tempo"));
5728 _("Define one bar"),
5729 _("Do you want to set the global tempo or add a new tempo marker?"),
5733 c.set_default_response (2);
5749 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
5750 if the marker is at the region starter, change it, otherwise add
5755 begin_reversible_command (_("set tempo from region"));
5756 XMLNode& before (_session->tempo_map().get_state());
5759 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
5760 } else if (t.frame() == start) {
5761 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
5763 _session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
5766 XMLNode& after (_session->tempo_map().get_state());
5768 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
5769 commit_reversible_command ();
5773 Editor::split_region_at_transients ()
5775 AnalysisFeatureList positions;
5777 RegionSelection rs = get_regions_from_selection_and_entered ();
5779 if (!_session || rs.empty()) {
5783 _session->begin_reversible_command (_("split regions"));
5785 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
5787 RegionSelection::iterator tmp;
5792 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
5794 if (ar && (ar->get_transients (positions) == 0)) {
5795 split_region_at_points ((*i)->region(), positions, true);
5802 _session->commit_reversible_command ();
5807 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
5809 bool use_rhythmic_rodent = false;
5811 boost::shared_ptr<Playlist> pl = r->playlist();
5813 list<boost::shared_ptr<Region> > new_regions;
5819 if (positions.empty()) {
5824 if (positions.size() > 20 && can_ferret) {
5825 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);
5826 MessageDialog msg (msgstr,
5829 Gtk::BUTTONS_OK_CANCEL);
5832 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
5833 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
5835 msg.set_secondary_text (_("Press OK to continue with this split operation"));
5838 msg.set_title (_("Excessive split?"));
5841 int response = msg.run();
5847 case RESPONSE_APPLY:
5848 use_rhythmic_rodent = true;
5855 if (use_rhythmic_rodent) {
5856 show_rhythm_ferret ();
5860 AnalysisFeatureList::const_iterator x;
5862 pl->clear_changes ();
5863 pl->clear_owned_changes ();
5865 x = positions.begin();
5867 if (x == positions.end()) {
5872 pl->remove_region (r);
5876 while (x != positions.end()) {
5878 /* deal with positons that are out of scope of present region bounds */
5879 if (*x <= 0 || *x > r->length()) {
5884 /* file start = original start + how far we from the initial position ?
5887 framepos_t file_start = r->start() + pos;
5889 /* length = next position - current position
5892 framepos_t len = (*x) - pos;
5894 /* XXX we do we really want to allow even single-sample regions?
5895 shouldn't we have some kind of lower limit on region size?
5904 if (RegionFactory::region_name (new_name, r->name())) {
5908 /* do NOT announce new regions 1 by one, just wait till they are all done */
5912 plist.add (ARDOUR::Properties::start, file_start);
5913 plist.add (ARDOUR::Properties::length, len);
5914 plist.add (ARDOUR::Properties::name, new_name);
5915 plist.add (ARDOUR::Properties::layer, 0);
5917 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5918 /* because we set annouce to false, manually add the new region to the
5921 RegionFactory::map_add (nr);
5923 pl->add_region (nr, r->position() + pos);
5926 new_regions.push_front(nr);
5935 RegionFactory::region_name (new_name, r->name());
5937 /* Add the final region */
5940 plist.add (ARDOUR::Properties::start, r->start() + pos);
5941 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
5942 plist.add (ARDOUR::Properties::name, new_name);
5943 plist.add (ARDOUR::Properties::layer, 0);
5945 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
5946 /* because we set annouce to false, manually add the new region to the
5949 RegionFactory::map_add (nr);
5950 pl->add_region (nr, r->position() + pos);
5953 new_regions.push_front(nr);
5958 /* We might have removed regions, which alters other regions' layering_index,
5959 so we need to do a recursive diff here.
5961 vector<Command*> cmds;
5963 _session->add_commands (cmds);
5965 _session->add_command (new StatefulDiffCommand (pl));
5969 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
5970 set_selected_regionview_from_region_list ((*i), Selection::Add);
5976 Editor::place_transient()
5982 RegionSelection rs = get_regions_from_selection_and_edit_point ();
5988 framepos_t where = get_preferred_edit_position();
5990 _session->begin_reversible_command (_("place transient"));
5992 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5993 framepos_t position = (*r)->region()->position();
5994 (*r)->region()->add_transient(where - position);
5997 _session->commit_reversible_command ();
6001 Editor::remove_transient(ArdourCanvas::Item* item)
6007 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6010 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6011 _arv->remove_transient (*(float*) _line->get_data ("position"));
6015 Editor::snap_regions_to_grid ()
6017 list <boost::shared_ptr<Playlist > > used_playlists;
6019 RegionSelection rs = get_regions_from_selection_and_entered ();
6021 if (!_session || rs.empty()) {
6025 _session->begin_reversible_command (_("snap regions to grid"));
6027 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6029 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6031 if (!pl->frozen()) {
6032 /* we haven't seen this playlist before */
6034 /* remember used playlists so we can thaw them later */
6035 used_playlists.push_back(pl);
6039 framepos_t start_frame = (*r)->region()->first_frame ();
6040 snap_to (start_frame);
6041 (*r)->region()->set_position (start_frame);
6044 while (used_playlists.size() > 0) {
6045 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6047 used_playlists.pop_front();
6050 _session->commit_reversible_command ();
6054 Editor::close_region_gaps ()
6056 list <boost::shared_ptr<Playlist > > used_playlists;
6058 RegionSelection rs = get_regions_from_selection_and_entered ();
6060 if (!_session || rs.empty()) {
6064 Dialog dialog (_("Close Region Gaps"));
6067 table.set_spacings (12);
6068 table.set_border_width (12);
6069 Label* l = manage (left_aligned_label (_("Crossfade length")));
6070 table.attach (*l, 0, 1, 0, 1);
6072 SpinButton spin_crossfade (1, 0);
6073 spin_crossfade.set_range (0, 15);
6074 spin_crossfade.set_increments (1, 1);
6075 spin_crossfade.set_value (5);
6076 table.attach (spin_crossfade, 1, 2, 0, 1);
6078 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
6080 l = manage (left_aligned_label (_("Pull-back length")));
6081 table.attach (*l, 0, 1, 1, 2);
6083 SpinButton spin_pullback (1, 0);
6084 spin_pullback.set_range (0, 100);
6085 spin_pullback.set_increments (1, 1);
6086 spin_pullback.set_value(30);
6087 table.attach (spin_pullback, 1, 2, 1, 2);
6089 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
6091 dialog.get_vbox()->pack_start (table);
6092 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
6093 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
6096 if (dialog.run () == RESPONSE_CANCEL) {
6100 framepos_t crossfade_len = spin_crossfade.get_value();
6101 framepos_t pull_back_frames = spin_pullback.get_value();
6103 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
6104 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
6106 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
6108 _session->begin_reversible_command (_("close region gaps"));
6111 boost::shared_ptr<Region> last_region;
6113 rs.sort_by_position_and_track();
6115 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6117 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6119 if (!pl->frozen()) {
6120 /* we haven't seen this playlist before */
6122 /* remember used playlists so we can thaw them later */
6123 used_playlists.push_back(pl);
6127 framepos_t position = (*r)->region()->position();
6129 if (idx == 0 || position < last_region->position()){
6130 last_region = (*r)->region();
6135 (*r)->region()->trim_front( (position - pull_back_frames));
6136 last_region->trim_end( (position - pull_back_frames + crossfade_len));
6138 last_region = (*r)->region();
6143 while (used_playlists.size() > 0) {
6144 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6146 used_playlists.pop_front();
6149 _session->commit_reversible_command ();
6153 Editor::tab_to_transient (bool forward)
6155 AnalysisFeatureList positions;
6157 RegionSelection rs = get_regions_from_selection_and_entered ();
6163 framepos_t pos = _session->audible_frame ();
6165 if (!selection->tracks.empty()) {
6167 /* don't waste time searching for transients in duplicate playlists.
6170 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6172 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
6174 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
6177 boost::shared_ptr<Track> tr = rtv->track();
6179 boost::shared_ptr<Playlist> pl = tr->playlist ();
6181 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
6184 positions.push_back (result);
6197 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6198 (*r)->region()->get_transients (positions);
6202 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
6205 AnalysisFeatureList::iterator x;
6207 for (x = positions.begin(); x != positions.end(); ++x) {
6213 if (x != positions.end ()) {
6214 _session->request_locate (*x);
6218 AnalysisFeatureList::reverse_iterator x;
6220 for (x = positions.rbegin(); x != positions.rend(); ++x) {
6226 if (x != positions.rend ()) {
6227 _session->request_locate (*x);
6233 Editor::playhead_forward_to_grid ()
6235 if (!_session) return;
6236 framepos_t pos = playhead_cursor->current_frame;
6237 if (pos < max_framepos - 1) {
6239 snap_to_internal (pos, 1, false);
6240 _session->request_locate (pos);
6246 Editor::playhead_backward_to_grid ()
6248 if (!_session) return;
6249 framepos_t pos = playhead_cursor->current_frame;
6252 snap_to_internal (pos, -1, false);
6253 _session->request_locate (pos);
6258 Editor::set_track_height (Height h)
6260 TrackSelection& ts (selection->tracks);
6262 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6263 (*x)->set_height_enum (h);
6268 Editor::toggle_tracks_active ()
6270 TrackSelection& ts (selection->tracks);
6272 bool target = false;
6278 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6279 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
6283 target = !rtv->_route->active();
6286 rtv->_route->set_active (target, this);
6292 Editor::remove_tracks ()
6294 TrackSelection& ts (selection->tracks);
6300 vector<string> choices;
6304 const char* trackstr;
6306 vector<boost::shared_ptr<Route> > routes;
6307 bool special_bus = false;
6309 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
6310 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
6312 if (rtv->is_track()) {
6318 routes.push_back (rtv->_route);
6320 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
6325 if (special_bus && !Config->get_allow_special_bus_removal()) {
6326 MessageDialog msg (_("That would be bad news ...."),
6330 msg.set_secondary_text (string_compose (_(
6331 "Removing the master or monitor bus is such a bad idea\n\
6332 that %1 is not going to allow it.\n\
6334 If you really want to do this sort of thing\n\
6335 edit your ardour.rc file to set the\n\
6336 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
6343 if (ntracks + nbusses == 0) {
6348 trackstr = _("tracks");
6350 trackstr = _("track");
6354 busstr = _("busses");
6361 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
6362 "(You may also lose the playlists associated with the %2)\n\n"
6363 "This action cannot be undone, and the session file will be overwritten!"),
6364 ntracks, trackstr, nbusses, busstr);
6366 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
6367 "(You may also lose the playlists associated with the %2)\n\n"
6368 "This action cannot be undone, and the session file will be overwritten!"),
6371 } else if (nbusses) {
6372 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
6373 "This action cannot be undon, and the session file will be overwritten"),
6377 choices.push_back (_("No, do nothing."));
6378 if (ntracks + nbusses > 1) {
6379 choices.push_back (_("Yes, remove them."));
6381 choices.push_back (_("Yes, remove it."));
6386 title = string_compose (_("Remove %1"), trackstr);
6388 title = string_compose (_("Remove %1"), busstr);
6391 Choice prompter (title, prompt, choices);
6393 if (prompter.run () != 1) {
6397 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
6398 _session->remove_route (*x);
6403 Editor::do_insert_time ()
6405 if (selection->tracks.empty()) {
6409 InsertTimeDialog d (*this);
6410 int response = d.run ();
6412 if (response != RESPONSE_OK) {
6416 if (d.distance() == 0) {
6420 InsertTimeOption opt = d.intersected_region_action ();
6423 get_preferred_edit_position(),
6429 d.move_glued_markers(),
6430 d.move_locked_markers(),
6436 Editor::insert_time (
6437 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
6438 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
6441 bool commit = false;
6443 if (Config->get_edit_mode() == Lock) {
6447 begin_reversible_command (_("insert time"));
6449 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
6451 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
6455 /* don't operate on any playlist more than once, which could
6456 * happen if "all playlists" is enabled, but there is more
6457 * than 1 track using playlists "from" a given track.
6460 set<boost::shared_ptr<Playlist> > pl;
6462 if (all_playlists) {
6463 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6465 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
6466 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
6471 if ((*x)->playlist ()) {
6472 pl.insert ((*x)->playlist ());
6476 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
6478 (*i)->clear_changes ();
6479 (*i)->clear_owned_changes ();
6481 if (opt == SplitIntersected) {
6485 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
6487 vector<Command*> cmds;
6489 _session->add_commands (cmds);
6491 _session->add_command (new StatefulDiffCommand (*i));
6496 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
6498 rtav->route ()->shift (pos, frames);
6506 XMLNode& before (_session->locations()->get_state());
6507 Locations::LocationList copy (_session->locations()->list());
6509 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
6511 Locations::LocationList::const_iterator tmp;
6513 bool const was_locked = (*i)->locked ();
6514 if (locked_markers_too) {
6518 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
6520 if ((*i)->start() >= pos) {
6521 (*i)->set_start ((*i)->start() + frames);
6522 if (!(*i)->is_mark()) {
6523 (*i)->set_end ((*i)->end() + frames);
6536 XMLNode& after (_session->locations()->get_state());
6537 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
6542 _session->tempo_map().insert_time (pos, frames);
6546 commit_reversible_command ();
6551 Editor::fit_selected_tracks ()
6553 if (!selection->tracks.empty()) {
6554 fit_tracks (selection->tracks);
6558 /* no selected tracks - use tracks with selected regions */
6560 if (!selection->regions.empty()) {
6561 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
6562 tvl.push_back (&(*r)->get_time_axis_view ());
6568 } else if (internal_editing()) {
6569 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
6572 if (entered_track) {
6573 tvl.push_back (entered_track);
6581 Editor::fit_tracks (TrackViewList & tracks)
6583 if (tracks.empty()) {
6587 uint32_t child_heights = 0;
6588 int visible_tracks = 0;
6590 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
6592 if (!(*t)->marked_for_display()) {
6596 child_heights += (*t)->effective_height() - (*t)->current_height();
6600 uint32_t h = (uint32_t) floor ((_canvas_height - child_heights - canvas_timebars_vsize) / visible_tracks);
6601 double first_y_pos = DBL_MAX;
6603 if (h < TimeAxisView::preset_height (HeightSmall)) {
6604 MessageDialog msg (*this, _("There are too many tracks to fit in the current window"));
6605 /* too small to be displayed */
6609 undo_visual_stack.push_back (current_visual_state (true));
6610 no_save_visual = true;
6612 /* build a list of all tracks, including children */
6615 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6617 TimeAxisView::Children c = (*i)->get_child_list ();
6618 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
6619 all.push_back (j->get());
6623 /* operate on all tracks, hide unselected ones that are in the middle of selected ones */
6625 bool prev_was_selected = false;
6626 bool is_selected = tracks.contains (all.front());
6627 bool next_is_selected;
6629 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
6631 TrackViewList::iterator next;
6636 if (next != all.end()) {
6637 next_is_selected = tracks.contains (*next);
6639 next_is_selected = false;
6642 if ((*t)->marked_for_display ()) {
6644 (*t)->set_height (h);
6645 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
6647 if (prev_was_selected && next_is_selected) {
6648 hide_track_in_display (*t);
6653 prev_was_selected = is_selected;
6654 is_selected = next_is_selected;
6658 set the controls_layout height now, because waiting for its size
6659 request signal handler will cause the vertical adjustment setting to fail
6662 controls_layout.property_height () = full_canvas_height - canvas_timebars_vsize;
6663 vertical_adjustment.set_value (first_y_pos);
6665 redo_visual_stack.push_back (current_visual_state (true));
6669 Editor::save_visual_state (uint32_t n)
6671 while (visual_states.size() <= n) {
6672 visual_states.push_back (0);
6675 if (visual_states[n] != 0) {
6676 delete visual_states[n];
6679 visual_states[n] = current_visual_state (true);
6684 Editor::goto_visual_state (uint32_t n)
6686 if (visual_states.size() <= n) {
6690 if (visual_states[n] == 0) {
6694 use_visual_state (*visual_states[n]);
6698 Editor::start_visual_state_op (uint32_t n)
6700 save_visual_state (n);
6702 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
6704 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
6705 pup->set_text (buf);
6710 Editor::cancel_visual_state_op (uint32_t n)
6712 goto_visual_state (n);
6716 Editor::toggle_region_mute ()
6718 if (_ignore_region_action) {
6722 RegionSelection rs = get_regions_from_selection_and_entered ();
6728 if (rs.size() > 1) {
6729 begin_reversible_command (_("mute regions"));
6731 begin_reversible_command (_("mute region"));
6734 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6736 (*i)->region()->playlist()->clear_changes ();
6737 (*i)->region()->set_muted (!(*i)->region()->muted ());
6738 _session->add_command (new StatefulDiffCommand ((*i)->region()->playlist()));
6742 commit_reversible_command ();
6746 Editor::combine_regions ()
6748 /* foreach track with selected regions, take all selected regions
6749 and join them into a new region containing the subregions (as a
6753 typedef set<RouteTimeAxisView*> RTVS;
6756 if (selection->regions.empty()) {
6760 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6761 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6764 tracks.insert (rtv);
6768 begin_reversible_command (_("combine regions"));
6770 vector<RegionView*> new_selection;
6772 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6775 if ((rv = (*i)->combine_regions ()) != 0) {
6776 new_selection.push_back (rv);
6780 selection->clear_regions ();
6781 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
6782 selection->add (*i);
6785 commit_reversible_command ();
6789 Editor::uncombine_regions ()
6791 typedef set<RouteTimeAxisView*> RTVS;
6794 if (selection->regions.empty()) {
6798 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
6799 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
6802 tracks.insert (rtv);
6806 begin_reversible_command (_("uncombine regions"));
6808 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
6809 (*i)->uncombine_regions ();
6812 commit_reversible_command ();