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 */
31 #include <gtkmm/messagedialog.h>
33 #include "pbd/error.h"
34 #include "pbd/basename.h"
35 #include "pbd/pthread_utils.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/unwind.h"
38 #include "pbd/whitespace.h"
39 #include "pbd/stateful_diff_command.h"
41 #include "gtkmm2ext/utils.h"
43 #include "widgets/choice.h"
44 #include "widgets/popup.h"
45 #include "widgets/prompter.h"
47 #include "ardour/audio_track.h"
48 #include "ardour/audioregion.h"
49 #include "ardour/boost_debug.h"
50 #include "ardour/dB.h"
51 #include "ardour/location.h"
52 #include "ardour/midi_region.h"
53 #include "ardour/midi_track.h"
54 #include "ardour/operations.h"
55 #include "ardour/playlist_factory.h"
56 #include "ardour/profile.h"
57 #include "ardour/quantize.h"
58 #include "ardour/legatize.h"
59 #include "ardour/region_factory.h"
60 #include "ardour/reverse.h"
61 #include "ardour/session.h"
62 #include "ardour/session_playlists.h"
63 #include "ardour/strip_silence.h"
64 #include "ardour/transient_detector.h"
65 #include "ardour/transpose.h"
66 #include "ardour/vca_manager.h"
68 #include "canvas/canvas.h"
71 #include "audio_region_view.h"
72 #include "audio_streamview.h"
73 #include "audio_time_axis.h"
74 #include "automation_region_view.h"
75 #include "automation_time_axis.h"
76 #include "control_point.h"
80 #include "editor_cursors.h"
81 #include "editor_drag.h"
82 #include "editor_regions.h"
83 #include "editor_routes.h"
84 #include "gui_thread.h"
85 #include "insert_remove_time_dialog.h"
86 #include "interthread_progress_window.h"
87 #include "item_counts.h"
89 #include "midi_region_view.h"
91 #include "mixer_strip.h"
92 #include "mouse_cursors.h"
93 #include "normalize_dialog.h"
95 #include "paste_context.h"
96 #include "patch_change_dialog.h"
97 #include "quantize_dialog.h"
98 #include "region_gain_line.h"
99 #include "rgb_macros.h"
100 #include "route_time_axis.h"
101 #include "selection.h"
102 #include "selection_templates.h"
103 #include "streamview.h"
104 #include "strip_silence_dialog.h"
105 #include "time_axis_view.h"
107 #include "transpose_dialog.h"
108 #include "transform_dialog.h"
109 #include "ui_config.h"
110 #include "vca_time_axis.h"
112 #include "pbd/i18n.h"
115 using namespace ARDOUR;
118 using namespace Gtkmm2ext;
119 using namespace ArdourWidgets;
120 using namespace Editing;
121 using Gtkmm2ext::Keyboard;
123 /***********************************************************************
125 ***********************************************************************/
128 Editor::undo (uint32_t n)
130 if (_session && _session->actively_recording()) {
131 /* no undo allowed while recording. Session will check also,
132 but we don't even want to get to that.
137 if (_drags->active ()) {
143 if (_session->undo_depth() == 0) {
144 undo_action->set_sensitive(false);
146 redo_action->set_sensitive(true);
147 begin_selection_op_history ();
152 Editor::redo (uint32_t n)
154 if (_session && _session->actively_recording()) {
155 /* no redo allowed while recording. Session will check also,
156 but we don't even want to get to that.
161 if (_drags->active ()) {
167 if (_session->redo_depth() == 0) {
168 redo_action->set_sensitive(false);
170 undo_action->set_sensitive(true);
171 begin_selection_op_history ();
176 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
180 RegionSelection pre_selected_regions = selection->regions;
181 bool working_on_selection = !pre_selected_regions.empty();
183 list<boost::shared_ptr<Playlist> > used_playlists;
184 list<RouteTimeAxisView*> used_trackviews;
186 if (regions.empty()) {
190 begin_reversible_command (_("split"));
192 // if splitting a single region, and snap-to is using
193 // region boundaries, don't pay attention to them
195 if (regions.size() == 1) {
196 switch (_snap_type) {
197 case SnapToRegionStart:
198 case SnapToRegionSync:
199 case SnapToRegionEnd:
212 EditorFreeze(); /* Emit Signal */
215 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
217 RegionSelection::iterator tmp;
219 /* XXX this test needs to be more complicated, to make sure we really
220 have something to split.
223 if (!(*a)->region()->covers (where.frame)) {
231 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
239 /* we haven't seen this playlist before */
241 /* remember used playlists so we can thaw them later */
242 used_playlists.push_back(pl);
244 TimeAxisView& tv = (*a)->get_time_axis_view();
245 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
247 used_trackviews.push_back (rtv);
254 pl->clear_changes ();
255 pl->split_region ((*a)->region(), where);
256 _session->add_command (new StatefulDiffCommand (pl));
262 latest_regionviews.clear ();
264 vector<sigc::connection> region_added_connections;
266 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
267 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
270 while (used_playlists.size() > 0) {
271 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
273 used_playlists.pop_front();
276 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
281 EditorThaw(); /* Emit Signal */
284 if (working_on_selection) {
285 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
287 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
288 /* There are three classes of regions that we might want selected after
289 splitting selected regions:
290 - regions selected before the split operation, and unaffected by it
291 - newly-created regions before the split
292 - newly-created regions after the split
295 if (rsas & Existing) {
296 // region selections that existed before the split.
297 selection->add ( pre_selected_regions );
300 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
301 if ((*ri)->region()->position() < where.frame) {
302 // new regions created before the split
303 if (rsas & NewlyCreatedLeft) {
304 selection->add (*ri);
307 // new regions created after the split
308 if (rsas & NewlyCreatedRight) {
309 selection->add (*ri);
314 if( working_on_selection ) {
315 selection->add (latest_regionviews); //these are the new regions created after the split
319 commit_reversible_command ();
322 /** Move one extreme of the current range selection. If more than one range is selected,
323 * the start of the earliest range or the end of the latest range is moved.
325 * @param move_end true to move the end of the current range selection, false to move
327 * @param next true to move the extreme to the next region boundary, false to move to
331 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
333 if (selection->time.start() == selection->time.end_frame()) {
337 framepos_t start = selection->time.start ();
338 framepos_t end = selection->time.end_frame ();
340 /* the position of the thing we may move */
341 framepos_t pos = move_end ? end : start;
342 int dir = next ? 1 : -1;
344 /* so we don't find the current region again */
345 if (dir > 0 || pos > 0) {
349 framepos_t const target = get_region_boundary (pos, dir, true, false);
364 begin_reversible_selection_op (_("alter selection"));
365 selection->set_preserving_all_ranges (start, end);
366 commit_reversible_selection_op ();
370 Editor::nudge_forward_release (GdkEventButton* ev)
372 if (ev->state & Keyboard::PrimaryModifier) {
373 nudge_forward (false, true);
375 nudge_forward (false, false);
381 Editor::nudge_backward_release (GdkEventButton* ev)
383 if (ev->state & Keyboard::PrimaryModifier) {
384 nudge_backward (false, true);
386 nudge_backward (false, false);
393 Editor::nudge_forward (bool next, bool force_playhead)
396 framepos_t next_distance;
402 RegionSelection rs = get_regions_from_selection_and_entered ();
404 if (!force_playhead && !rs.empty()) {
406 begin_reversible_command (_("nudge regions forward"));
408 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
409 boost::shared_ptr<Region> r ((*i)->region());
411 distance = get_nudge_distance (r->position(), next_distance);
414 distance = next_distance;
418 r->set_position (r->position() + distance);
419 _session->add_command (new StatefulDiffCommand (r));
422 commit_reversible_command ();
425 } else if (!force_playhead && !selection->markers.empty()) {
428 bool in_command = false;
429 const int32_t divisions = get_grid_music_divisions (0);
431 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
433 Location* loc = find_location_from_marker ((*i), is_start);
437 XMLNode& before (loc->get_state());
440 distance = get_nudge_distance (loc->start(), next_distance);
442 distance = next_distance;
444 if (max_framepos - distance > loc->start() + loc->length()) {
445 loc->set_start (loc->start() + distance, false, true, divisions);
447 loc->set_start (max_framepos - loc->length(), false, true, divisions);
450 distance = get_nudge_distance (loc->end(), next_distance);
452 distance = next_distance;
454 if (max_framepos - distance > loc->end()) {
455 loc->set_end (loc->end() + distance, false, true, divisions);
457 loc->set_end (max_framepos, false, true, divisions);
459 if (loc->is_session_range()) {
460 _session->set_end_is_free (false);
464 begin_reversible_command (_("nudge location forward"));
467 XMLNode& after (loc->get_state());
468 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
473 commit_reversible_command ();
476 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
477 _session->request_locate (playhead_cursor->current_frame () + distance);
482 Editor::nudge_backward (bool next, bool force_playhead)
485 framepos_t next_distance;
491 RegionSelection rs = get_regions_from_selection_and_entered ();
493 if (!force_playhead && !rs.empty()) {
495 begin_reversible_command (_("nudge regions backward"));
497 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
498 boost::shared_ptr<Region> r ((*i)->region());
500 distance = get_nudge_distance (r->position(), next_distance);
503 distance = next_distance;
508 if (r->position() > distance) {
509 r->set_position (r->position() - distance);
513 _session->add_command (new StatefulDiffCommand (r));
516 commit_reversible_command ();
518 } else if (!force_playhead && !selection->markers.empty()) {
521 bool in_command = false;
523 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
525 Location* loc = find_location_from_marker ((*i), is_start);
529 XMLNode& before (loc->get_state());
532 distance = get_nudge_distance (loc->start(), next_distance);
534 distance = next_distance;
536 if (distance < loc->start()) {
537 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
539 loc->set_start (0, false, true, get_grid_music_divisions(0));
542 distance = get_nudge_distance (loc->end(), next_distance);
545 distance = next_distance;
548 if (distance < loc->end() - loc->length()) {
549 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
551 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
553 if (loc->is_session_range()) {
554 _session->set_end_is_free (false);
558 begin_reversible_command (_("nudge location forward"));
561 XMLNode& after (loc->get_state());
562 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
566 commit_reversible_command ();
571 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
573 if (playhead_cursor->current_frame () > distance) {
574 _session->request_locate (playhead_cursor->current_frame () - distance);
576 _session->goto_start();
582 Editor::nudge_forward_capture_offset ()
584 RegionSelection rs = get_regions_from_selection_and_entered ();
586 if (!_session || rs.empty()) {
590 begin_reversible_command (_("nudge forward"));
592 framepos_t const distance = _session->worst_output_latency();
594 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
595 boost::shared_ptr<Region> r ((*i)->region());
598 r->set_position (r->position() + distance);
599 _session->add_command(new StatefulDiffCommand (r));
602 commit_reversible_command ();
606 Editor::nudge_backward_capture_offset ()
608 RegionSelection rs = get_regions_from_selection_and_entered ();
610 if (!_session || rs.empty()) {
614 begin_reversible_command (_("nudge backward"));
616 framepos_t const distance = _session->worst_output_latency();
618 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
619 boost::shared_ptr<Region> r ((*i)->region());
623 if (r->position() > distance) {
624 r->set_position (r->position() - distance);
628 _session->add_command(new StatefulDiffCommand (r));
631 commit_reversible_command ();
634 struct RegionSelectionPositionSorter {
635 bool operator() (RegionView* a, RegionView* b) {
636 return a->region()->position() < b->region()->position();
641 Editor::sequence_regions ()
644 framepos_t r_end_prev;
652 RegionSelection rs = get_regions_from_selection_and_entered ();
653 rs.sort(RegionSelectionPositionSorter());
657 bool in_command = false;
659 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
660 boost::shared_ptr<Region> r ((*i)->region());
668 if(r->position_locked())
675 r->set_position(r_end_prev);
679 begin_reversible_command (_("sequence regions"));
682 _session->add_command (new StatefulDiffCommand (r));
684 r_end=r->position() + r->length();
690 commit_reversible_command ();
699 Editor::move_to_start ()
701 _session->goto_start ();
705 Editor::move_to_end ()
708 _session->request_locate (_session->current_end_frame());
712 Editor::build_region_boundary_cache ()
715 vector<RegionPoint> interesting_points;
716 boost::shared_ptr<Region> r;
717 TrackViewList tracks;
720 region_boundary_cache.clear ();
726 bool maybe_first_frame = false;
728 switch (_snap_type) {
729 case SnapToRegionStart:
730 interesting_points.push_back (Start);
731 maybe_first_frame = true;
733 case SnapToRegionEnd:
734 interesting_points.push_back (End);
736 case SnapToRegionSync:
737 interesting_points.push_back (SyncPoint);
739 case SnapToRegionBoundary:
740 interesting_points.push_back (Start);
741 interesting_points.push_back (End);
742 maybe_first_frame = true;
745 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
746 abort(); /*NOTREACHED*/
750 TimeAxisView *ontrack = 0;
753 if (!selection->tracks.empty()) {
754 tlist = selection->tracks.filter_to_unique_playlists ();
756 tlist = track_views.filter_to_unique_playlists ();
759 if (maybe_first_frame) {
760 TrackViewList::const_iterator i;
761 for (i = tlist.begin(); i != tlist.end(); ++i) {
762 boost::shared_ptr<Playlist> pl = (*i)->playlist();
763 if (pl && pl->count_regions_at (0)) {
764 region_boundary_cache.push_back (0);
770 while (pos < _session->current_end_frame() && !at_end) {
773 framepos_t lpos = max_framepos;
775 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
777 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
778 if (*p == interesting_points.back()) {
781 /* move to next point type */
787 rpos = r->first_frame();
791 rpos = r->last_frame();
795 rpos = r->sync_position ();
803 RouteTimeAxisView *rtav;
805 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
806 if (rtav->track() != 0) {
807 speed = rtav->track()->speed();
811 rpos = track_frame_to_session_frame (rpos, speed);
817 /* prevent duplicates, but we don't use set<> because we want to be able
821 vector<framepos_t>::iterator ri;
823 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
829 if (ri == region_boundary_cache.end()) {
830 region_boundary_cache.push_back (rpos);
837 /* finally sort to be sure that the order is correct */
839 sort (region_boundary_cache.begin(), region_boundary_cache.end());
842 boost::shared_ptr<Region>
843 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
845 TrackViewList::iterator i;
846 framepos_t closest = max_framepos;
847 boost::shared_ptr<Region> ret;
851 framepos_t track_frame;
852 RouteTimeAxisView *rtav;
854 for (i = tracks.begin(); i != tracks.end(); ++i) {
857 boost::shared_ptr<Region> r;
860 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
861 if (rtav->track()!=0)
862 track_speed = rtav->track()->speed();
865 track_frame = session_frame_to_track_frame(frame, track_speed);
867 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
873 rpos = r->first_frame ();
877 rpos = r->last_frame ();
881 rpos = r->sync_position ();
885 // rpos is a "track frame", converting it to "_session frame"
886 rpos = track_frame_to_session_frame(rpos, track_speed);
889 distance = rpos - frame;
891 distance = frame - rpos;
894 if (distance < closest) {
906 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
908 framecnt_t distance = max_framepos;
909 framepos_t current_nearest = -1;
911 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
912 framepos_t contender;
915 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
921 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
925 d = ::llabs (pos - contender);
928 current_nearest = contender;
933 return current_nearest;
937 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
942 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
944 if (!selection->tracks.empty()) {
946 target = find_next_region_boundary (pos, dir, selection->tracks);
950 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
951 get_onscreen_tracks (tvl);
952 target = find_next_region_boundary (pos, dir, tvl);
954 target = find_next_region_boundary (pos, dir, track_views);
960 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
961 get_onscreen_tracks (tvl);
962 target = find_next_region_boundary (pos, dir, tvl);
964 target = find_next_region_boundary (pos, dir, track_views);
972 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
974 framepos_t pos = playhead_cursor->current_frame ();
981 // so we don't find the current region again..
982 if (dir > 0 || pos > 0) {
986 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
990 _session->request_locate (target);
994 Editor::cursor_to_next_region_boundary (bool with_selection)
996 cursor_to_region_boundary (with_selection, 1);
1000 Editor::cursor_to_previous_region_boundary (bool with_selection)
1002 cursor_to_region_boundary (with_selection, -1);
1006 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
1008 boost::shared_ptr<Region> r;
1009 framepos_t pos = cursor->current_frame ();
1015 TimeAxisView *ontrack = 0;
1017 // so we don't find the current region again..
1021 if (!selection->tracks.empty()) {
1023 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1025 } else if (clicked_axisview) {
1028 t.push_back (clicked_axisview);
1030 r = find_next_region (pos, point, dir, t, &ontrack);
1034 r = find_next_region (pos, point, dir, track_views, &ontrack);
1043 pos = r->first_frame ();
1047 pos = r->last_frame ();
1051 pos = r->sync_position ();
1056 RouteTimeAxisView *rtav;
1058 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1059 if (rtav->track() != 0) {
1060 speed = rtav->track()->speed();
1064 pos = track_frame_to_session_frame(pos, speed);
1066 if (cursor == playhead_cursor) {
1067 _session->request_locate (pos);
1069 cursor->set_position (pos);
1074 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1076 cursor_to_region_point (cursor, point, 1);
1080 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1082 cursor_to_region_point (cursor, point, -1);
1086 Editor::cursor_to_selection_start (EditorCursor *cursor)
1090 switch (mouse_mode) {
1092 if (!selection->regions.empty()) {
1093 pos = selection->regions.start();
1098 if (!selection->time.empty()) {
1099 pos = selection->time.start ();
1107 if (cursor == playhead_cursor) {
1108 _session->request_locate (pos);
1110 cursor->set_position (pos);
1115 Editor::cursor_to_selection_end (EditorCursor *cursor)
1119 switch (mouse_mode) {
1121 if (!selection->regions.empty()) {
1122 pos = selection->regions.end_frame();
1127 if (!selection->time.empty()) {
1128 pos = selection->time.end_frame ();
1136 if (cursor == playhead_cursor) {
1137 _session->request_locate (pos);
1139 cursor->set_position (pos);
1144 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1154 if (selection->markers.empty()) {
1158 if (!mouse_frame (mouse, ignored)) {
1162 add_location_mark (mouse);
1165 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1169 framepos_t pos = loc->start();
1171 // so we don't find the current region again..
1172 if (dir > 0 || pos > 0) {
1176 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1180 loc->move_to (target, 0);
1184 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1186 selected_marker_to_region_boundary (with_selection, 1);
1190 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1192 selected_marker_to_region_boundary (with_selection, -1);
1196 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1198 boost::shared_ptr<Region> r;
1203 if (!_session || selection->markers.empty()) {
1207 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1211 TimeAxisView *ontrack = 0;
1215 // so we don't find the current region again..
1219 if (!selection->tracks.empty()) {
1221 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1225 r = find_next_region (pos, point, dir, track_views, &ontrack);
1234 pos = r->first_frame ();
1238 pos = r->last_frame ();
1242 pos = r->adjust_to_sync (r->first_frame());
1247 RouteTimeAxisView *rtav;
1249 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1250 if (rtav->track() != 0) {
1251 speed = rtav->track()->speed();
1255 pos = track_frame_to_session_frame(pos, speed);
1257 loc->move_to (pos, 0);
1261 Editor::selected_marker_to_next_region_point (RegionPoint point)
1263 selected_marker_to_region_point (point, 1);
1267 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1269 selected_marker_to_region_point (point, -1);
1273 Editor::selected_marker_to_selection_start ()
1279 if (!_session || selection->markers.empty()) {
1283 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1287 switch (mouse_mode) {
1289 if (!selection->regions.empty()) {
1290 pos = selection->regions.start();
1295 if (!selection->time.empty()) {
1296 pos = selection->time.start ();
1304 loc->move_to (pos, 0);
1308 Editor::selected_marker_to_selection_end ()
1314 if (!_session || selection->markers.empty()) {
1318 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1322 switch (mouse_mode) {
1324 if (!selection->regions.empty()) {
1325 pos = selection->regions.end_frame();
1330 if (!selection->time.empty()) {
1331 pos = selection->time.end_frame ();
1339 loc->move_to (pos, 0);
1343 Editor::scroll_playhead (bool forward)
1345 framepos_t pos = playhead_cursor->current_frame ();
1346 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1349 if (pos == max_framepos) {
1353 if (pos < max_framepos - delta) {
1372 _session->request_locate (pos);
1376 Editor::cursor_align (bool playhead_to_edit)
1382 if (playhead_to_edit) {
1384 if (selection->markers.empty()) {
1388 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1391 const int32_t divisions = get_grid_music_divisions (0);
1392 /* move selected markers to playhead */
1394 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1397 Location* loc = find_location_from_marker (*i, ignored);
1399 if (loc->is_mark()) {
1400 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1402 loc->set (playhead_cursor->current_frame (),
1403 playhead_cursor->current_frame () + loc->length(), true, divisions);
1410 Editor::scroll_backward (float pages)
1412 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1413 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1416 if (leftmost_frame < cnt) {
1419 frame = leftmost_frame - cnt;
1422 reset_x_origin (frame);
1426 Editor::scroll_forward (float pages)
1428 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1429 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1432 if (max_framepos - cnt < leftmost_frame) {
1433 frame = max_framepos - cnt;
1435 frame = leftmost_frame + cnt;
1438 reset_x_origin (frame);
1442 Editor::scroll_tracks_down ()
1444 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1445 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1446 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1449 vertical_adjustment.set_value (vert_value);
1453 Editor::scroll_tracks_up ()
1455 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1459 Editor::scroll_tracks_down_line ()
1461 double vert_value = vertical_adjustment.get_value() + 60;
1463 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1464 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1467 vertical_adjustment.set_value (vert_value);
1471 Editor::scroll_tracks_up_line ()
1473 reset_y_origin (vertical_adjustment.get_value() - 60);
1477 Editor::select_topmost_track ()
1479 const double top_of_trackviews = vertical_adjustment.get_value();
1480 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1481 if ((*t)->hidden()) {
1484 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1486 selection->set (*t);
1493 Editor::scroll_down_one_track (bool skip_child_views)
1495 TrackViewList::reverse_iterator next = track_views.rend();
1496 const double top_of_trackviews = vertical_adjustment.get_value();
1498 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1499 if ((*t)->hidden()) {
1503 /* If this is the upper-most visible trackview, we want to display
1504 * the one above it (next)
1506 * Note that covers_y_position() is recursive and includes child views
1508 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1511 if (skip_child_views) {
1514 /* automation lane (one level, non-recursive)
1516 * - if no automation lane exists -> move to next tack
1517 * - if the first (here: bottom-most) matches -> move to next tack
1518 * - if no y-axis match is found -> the current track is at the top
1519 * -> move to last (here: top-most) automation lane
1521 TimeAxisView::Children kids = (*t)->get_child_list();
1522 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1524 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1525 if ((*ci)->hidden()) {
1529 std::pair<TimeAxisView*,double> dev;
1530 dev = (*ci)->covers_y_position (top_of_trackviews);
1532 /* some automation lane is currently at the top */
1533 if (ci == kids.rbegin()) {
1534 /* first (bottom-most) autmation lane is at the top.
1535 * -> move to next track
1544 if (nkid != kids.rend()) {
1545 ensure_time_axis_view_is_visible (**nkid, true);
1553 /* move to the track below the first one that covers the */
1555 if (next != track_views.rend()) {
1556 ensure_time_axis_view_is_visible (**next, true);
1564 Editor::scroll_up_one_track (bool skip_child_views)
1566 TrackViewList::iterator prev = track_views.end();
1567 double top_of_trackviews = vertical_adjustment.get_value ();
1569 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1571 if ((*t)->hidden()) {
1575 /* find the trackview at the top of the trackview group
1577 * Note that covers_y_position() is recursive and includes child views
1579 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1582 if (skip_child_views) {
1585 /* automation lane (one level, non-recursive)
1587 * - if no automation lane exists -> move to prev tack
1588 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1589 * (actually last automation lane of previous track, see below)
1590 * - if first (top-most) lane is at the top -> move to this track
1591 * - else move up one lane
1593 TimeAxisView::Children kids = (*t)->get_child_list();
1594 TimeAxisView::Children::iterator pkid = kids.end();
1596 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1597 if ((*ci)->hidden()) {
1601 std::pair<TimeAxisView*,double> dev;
1602 dev = (*ci)->covers_y_position (top_of_trackviews);
1604 /* some automation lane is currently at the top */
1605 if (ci == kids.begin()) {
1606 /* first (top-most) autmation lane is at the top.
1607 * jump directly to this track's top
1609 ensure_time_axis_view_is_visible (**t, true);
1612 else if (pkid != kids.end()) {
1613 /* some other automation lane is at the top.
1614 * move up to prev automation lane.
1616 ensure_time_axis_view_is_visible (**pkid, true);
1619 assert(0); // not reached
1630 if (prev != track_views.end()) {
1631 // move to bottom-most automation-lane of the previous track
1632 TimeAxisView::Children kids = (*prev)->get_child_list();
1633 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1634 if (!skip_child_views) {
1635 // find the last visible lane
1636 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1637 if (!(*ci)->hidden()) {
1643 if (pkid != kids.rend()) {
1644 ensure_time_axis_view_is_visible (**pkid, true);
1646 ensure_time_axis_view_is_visible (**prev, true);
1655 Editor::scroll_left_step ()
1657 framepos_t xdelta = (current_page_samples() / 8);
1659 if (leftmost_frame > xdelta) {
1660 reset_x_origin (leftmost_frame - xdelta);
1668 Editor::scroll_right_step ()
1670 framepos_t xdelta = (current_page_samples() / 8);
1672 if (max_framepos - xdelta > leftmost_frame) {
1673 reset_x_origin (leftmost_frame + xdelta);
1675 reset_x_origin (max_framepos - current_page_samples());
1680 Editor::scroll_left_half_page ()
1682 framepos_t xdelta = (current_page_samples() / 2);
1683 if (leftmost_frame > xdelta) {
1684 reset_x_origin (leftmost_frame - xdelta);
1691 Editor::scroll_right_half_page ()
1693 framepos_t xdelta = (current_page_samples() / 2);
1694 if (max_framepos - xdelta > leftmost_frame) {
1695 reset_x_origin (leftmost_frame + xdelta);
1697 reset_x_origin (max_framepos - current_page_samples());
1704 Editor::tav_zoom_step (bool coarser)
1706 DisplaySuspender ds;
1710 if (selection->tracks.empty()) {
1713 ts = &selection->tracks;
1716 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1717 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1718 tv->step_height (coarser);
1723 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1725 DisplaySuspender ds;
1729 if (selection->tracks.empty() || force_all) {
1732 ts = &selection->tracks;
1735 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1736 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1737 uint32_t h = tv->current_height ();
1742 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1747 tv->set_height (h + 5);
1753 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1755 Editing::ZoomFocus temp_focus = zoom_focus;
1756 zoom_focus = Editing::ZoomFocusMouse;
1757 temporal_zoom_step_scale (zoom_out, scale);
1758 zoom_focus = temp_focus;
1762 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1764 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1768 Editor::temporal_zoom_step (bool zoom_out)
1770 temporal_zoom_step_scale (zoom_out, 2.0);
1774 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1776 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1778 framecnt_t nspp = samples_per_pixel;
1782 if (nspp == samples_per_pixel) {
1787 if (nspp == samples_per_pixel) {
1792 //zoom-behavior-tweaks
1793 //limit our maximum zoom to the session gui extents value
1794 std::pair<framepos_t, framepos_t> ext = session_gui_extents();
1795 framecnt_t session_extents_pp = ( ext.second - ext.first ) / _visible_canvas_width;
1796 if (nspp > session_extents_pp)
1797 nspp = session_extents_pp;
1799 temporal_zoom (nspp);
1803 Editor::temporal_zoom (framecnt_t fpp)
1809 framepos_t current_page = current_page_samples();
1810 framepos_t current_leftmost = leftmost_frame;
1811 framepos_t current_rightmost;
1812 framepos_t current_center;
1813 framepos_t new_page_size;
1814 framepos_t half_page_size;
1815 framepos_t leftmost_after_zoom = 0;
1817 bool in_track_canvas;
1818 bool use_mouse_frame = true;
1822 if (fpp == samples_per_pixel) {
1826 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1827 // segfaults for lack of memory. If somebody decides this is not high enough I
1828 // believe it can be raisen to higher values but some limit must be in place.
1830 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1831 // all of which is used for the editor track displays. The whole day
1832 // would be 4147200000 samples, so 2592000 samples per pixel.
1834 nfpp = min (fpp, (framecnt_t) 2592000);
1835 nfpp = max ((framecnt_t) 1, nfpp);
1837 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1838 half_page_size = new_page_size / 2;
1840 switch (zoom_focus) {
1842 leftmost_after_zoom = current_leftmost;
1845 case ZoomFocusRight:
1846 current_rightmost = leftmost_frame + current_page;
1847 if (current_rightmost < new_page_size) {
1848 leftmost_after_zoom = 0;
1850 leftmost_after_zoom = current_rightmost - new_page_size;
1854 case ZoomFocusCenter:
1855 current_center = current_leftmost + (current_page/2);
1856 if (current_center < half_page_size) {
1857 leftmost_after_zoom = 0;
1859 leftmost_after_zoom = current_center - half_page_size;
1863 case ZoomFocusPlayhead:
1864 /* centre playhead */
1865 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1868 leftmost_after_zoom = 0;
1869 } else if (l > max_framepos) {
1870 leftmost_after_zoom = max_framepos - new_page_size;
1872 leftmost_after_zoom = (framepos_t) l;
1876 case ZoomFocusMouse:
1877 /* try to keep the mouse over the same point in the display */
1879 if (_drags->active()) {
1880 where = _drags->current_pointer_frame ();
1881 } else if (!mouse_frame (where, in_track_canvas)) {
1882 use_mouse_frame = false;
1885 if (use_mouse_frame) {
1886 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1889 leftmost_after_zoom = 0;
1890 } else if (l > max_framepos) {
1891 leftmost_after_zoom = max_framepos - new_page_size;
1893 leftmost_after_zoom = (framepos_t) l;
1896 /* use playhead instead */
1897 where = playhead_cursor->current_frame ();
1899 if (where < half_page_size) {
1900 leftmost_after_zoom = 0;
1902 leftmost_after_zoom = where - half_page_size;
1908 /* try to keep the edit point in the same place */
1909 where = get_preferred_edit_position ();
1913 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1916 leftmost_after_zoom = 0;
1917 } else if (l > max_framepos) {
1918 leftmost_after_zoom = max_framepos - new_page_size;
1920 leftmost_after_zoom = (framepos_t) l;
1924 /* edit point not defined */
1931 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1933 reposition_and_zoom (leftmost_after_zoom, nfpp);
1937 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1939 /* this func helps make sure we leave a little space
1940 at each end of the editor so that the zoom doesn't fit the region
1941 precisely to the screen.
1944 GdkScreen* screen = gdk_screen_get_default ();
1945 const gint pixwidth = gdk_screen_get_width (screen);
1946 const gint mmwidth = gdk_screen_get_width_mm (screen);
1947 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1948 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1950 const framepos_t range = end - start;
1951 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1952 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1954 if (start > extra_samples) {
1955 start -= extra_samples;
1960 if (max_framepos - extra_samples > end) {
1961 end += extra_samples;
1968 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1970 start = max_framepos;
1974 //ToDo: if notes are selected, set extents to that selection
1976 //ToDo: if control points are selected, set extents to that selection
1978 if ( !selection->regions.empty() ) {
1979 RegionSelection rs = get_regions_from_selection_and_entered ();
1981 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1983 if ((*i)->region()->position() < start) {
1984 start = (*i)->region()->position();
1987 if ((*i)->region()->last_frame() + 1 > end) {
1988 end = (*i)->region()->last_frame() + 1;
1992 } else if (!selection->time.empty()) {
1993 start = selection->time.start();
1994 end = selection->time.end_frame();
1996 ret = false; //no selection found
1999 if ((start == 0 && end == 0) || end < start) {
2008 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
2010 if (!selection) return;
2012 //ToDo: if notes are selected, zoom to that
2014 //ToDo: if control points are selected, zoom to that
2016 if (axes == Horizontal || axes == Both) {
2018 framepos_t start, end;
2019 if (get_selection_extents (start, end)) {
2020 calc_extra_zoom_edges (start, end);
2021 temporal_zoom_by_frame (start, end);
2025 if (axes == Vertical || axes == Both) {
2031 Editor::temporal_zoom_session ()
2033 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2036 framecnt_t start = _session->current_start_frame();
2037 framecnt_t end = _session->current_end_frame();
2039 if (_session->actively_recording () ) {
2040 framepos_t cur = playhead_cursor->current_frame ();
2042 /* recording beyond the end marker; zoom out
2043 * by 5 seconds more so that if 'follow
2044 * playhead' is active we don't immediately
2047 end = cur + _session->frame_rate() * 5;
2051 if ((start == 0 && end == 0) || end < start) {
2055 calc_extra_zoom_edges(start, end);
2057 temporal_zoom_by_frame (start, end);
2062 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2064 if (!_session) return;
2066 if ((start == 0 && end == 0) || end < start) {
2070 framepos_t range = end - start;
2072 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2074 framepos_t new_page = range;
2075 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2076 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2078 if (new_leftmost > middle) {
2082 if (new_leftmost < 0) {
2086 reposition_and_zoom (new_leftmost, new_fpp);
2090 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2096 framecnt_t range_before = frame - leftmost_frame;
2100 if (samples_per_pixel <= 1) {
2103 new_spp = samples_per_pixel + (samples_per_pixel/2);
2105 range_before += range_before/2;
2107 if (samples_per_pixel >= 1) {
2108 new_spp = samples_per_pixel - (samples_per_pixel/2);
2110 /* could bail out here since we cannot zoom any finer,
2111 but leave that to the equality test below
2113 new_spp = samples_per_pixel;
2116 range_before -= range_before/2;
2119 if (new_spp == samples_per_pixel) {
2123 /* zoom focus is automatically taken as @param frame when this
2127 framepos_t new_leftmost = frame - (framepos_t)range_before;
2129 if (new_leftmost > frame) {
2133 if (new_leftmost < 0) {
2137 reposition_and_zoom (new_leftmost, new_spp);
2142 Editor::choose_new_marker_name(string &name) {
2144 if (!UIConfiguration::instance().get_name_new_markers()) {
2145 /* don't prompt user for a new name */
2149 Prompter dialog (true);
2151 dialog.set_prompt (_("New Name:"));
2153 dialog.set_title (_("New Location Marker"));
2155 dialog.set_name ("MarkNameWindow");
2156 dialog.set_size_request (250, -1);
2157 dialog.set_position (Gtk::WIN_POS_MOUSE);
2159 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2160 dialog.set_initial_text (name);
2164 switch (dialog.run ()) {
2165 case RESPONSE_ACCEPT:
2171 dialog.get_result(name);
2178 Editor::add_location_from_selection ()
2182 if (selection->time.empty()) {
2186 if (_session == 0 || clicked_axisview == 0) {
2190 framepos_t start = selection->time[clicked_selection].start;
2191 framepos_t end = selection->time[clicked_selection].end;
2193 _session->locations()->next_available_name(rangename,"selection");
2194 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2196 begin_reversible_command (_("add marker"));
2198 XMLNode &before = _session->locations()->get_state();
2199 _session->locations()->add (location, true);
2200 XMLNode &after = _session->locations()->get_state();
2201 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2203 commit_reversible_command ();
2207 Editor::add_location_mark (framepos_t where)
2211 select_new_marker = true;
2213 _session->locations()->next_available_name(markername,"mark");
2214 if (!choose_new_marker_name(markername)) {
2217 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2218 begin_reversible_command (_("add marker"));
2220 XMLNode &before = _session->locations()->get_state();
2221 _session->locations()->add (location, true);
2222 XMLNode &after = _session->locations()->get_state();
2223 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2225 commit_reversible_command ();
2229 Editor::set_session_start_from_playhead ()
2235 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2236 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2238 XMLNode &before = loc->get_state();
2240 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2242 XMLNode &after = loc->get_state();
2244 begin_reversible_command (_("Set session start"));
2246 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2248 commit_reversible_command ();
2253 Editor::set_session_end_from_playhead ()
2259 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2260 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2262 XMLNode &before = loc->get_state();
2264 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2266 XMLNode &after = loc->get_state();
2268 begin_reversible_command (_("Set session start"));
2270 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2272 commit_reversible_command ();
2275 _session->set_end_is_free (false);
2280 Editor::toggle_location_at_playhead_cursor ()
2282 if (!do_remove_location_at_playhead_cursor())
2284 add_location_from_playhead_cursor();
2289 Editor::add_location_from_playhead_cursor ()
2291 add_location_mark (_session->audible_frame());
2295 Editor::do_remove_location_at_playhead_cursor ()
2297 bool removed = false;
2300 XMLNode &before = _session->locations()->get_state();
2302 //find location(s) at this time
2303 Locations::LocationList locs;
2304 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2305 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2306 if ((*i)->is_mark()) {
2307 _session->locations()->remove (*i);
2314 begin_reversible_command (_("remove marker"));
2315 XMLNode &after = _session->locations()->get_state();
2316 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2317 commit_reversible_command ();
2324 Editor::remove_location_at_playhead_cursor ()
2326 do_remove_location_at_playhead_cursor ();
2329 /** Add a range marker around each selected region */
2331 Editor::add_locations_from_region ()
2333 RegionSelection rs = get_regions_from_selection_and_entered ();
2338 bool commit = false;
2340 XMLNode &before = _session->locations()->get_state();
2342 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2344 boost::shared_ptr<Region> region = (*i)->region ();
2346 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2348 _session->locations()->add (location, true);
2353 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2354 XMLNode &after = _session->locations()->get_state();
2355 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2356 commit_reversible_command ();
2360 /** Add a single range marker around all selected regions */
2362 Editor::add_location_from_region ()
2364 RegionSelection rs = get_regions_from_selection_and_entered ();
2370 XMLNode &before = _session->locations()->get_state();
2374 if (rs.size() > 1) {
2375 _session->locations()->next_available_name(markername, "regions");
2377 RegionView* rv = *(rs.begin());
2378 boost::shared_ptr<Region> region = rv->region();
2379 markername = region->name();
2382 if (!choose_new_marker_name(markername)) {
2386 // single range spanning all selected
2387 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2388 _session->locations()->add (location, true);
2390 begin_reversible_command (_("add marker"));
2391 XMLNode &after = _session->locations()->get_state();
2392 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2393 commit_reversible_command ();
2399 Editor::jump_forward_to_mark ()
2405 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2411 _session->request_locate (pos, _session->transport_rolling());
2415 Editor::jump_backward_to_mark ()
2421 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2423 //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
2424 if ( _session->transport_rolling() ) {
2425 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2426 framepos_t prior = _session->locations()->first_mark_before ( pos );
2435 _session->request_locate (pos, _session->transport_rolling());
2441 framepos_t const pos = _session->audible_frame ();
2444 _session->locations()->next_available_name (markername, "mark");
2446 if (!choose_new_marker_name (markername)) {
2450 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2454 Editor::clear_markers ()
2457 begin_reversible_command (_("clear markers"));
2459 XMLNode &before = _session->locations()->get_state();
2460 _session->locations()->clear_markers ();
2461 XMLNode &after = _session->locations()->get_state();
2462 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2464 commit_reversible_command ();
2469 Editor::clear_ranges ()
2472 begin_reversible_command (_("clear ranges"));
2474 XMLNode &before = _session->locations()->get_state();
2476 _session->locations()->clear_ranges ();
2478 XMLNode &after = _session->locations()->get_state();
2479 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2481 commit_reversible_command ();
2486 Editor::clear_locations ()
2488 begin_reversible_command (_("clear locations"));
2490 XMLNode &before = _session->locations()->get_state();
2491 _session->locations()->clear ();
2492 XMLNode &after = _session->locations()->get_state();
2493 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2495 commit_reversible_command ();
2499 Editor::unhide_markers ()
2501 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2502 Location *l = (*i).first;
2503 if (l->is_hidden() && l->is_mark()) {
2504 l->set_hidden(false, this);
2510 Editor::unhide_ranges ()
2512 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2513 Location *l = (*i).first;
2514 if (l->is_hidden() && l->is_range_marker()) {
2515 l->set_hidden(false, this);
2520 /* INSERT/REPLACE */
2523 Editor::insert_region_list_selection (float times)
2525 RouteTimeAxisView *tv = 0;
2526 boost::shared_ptr<Playlist> playlist;
2528 if (clicked_routeview != 0) {
2529 tv = clicked_routeview;
2530 } else if (!selection->tracks.empty()) {
2531 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2534 } else if (entered_track != 0) {
2535 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2542 if ((playlist = tv->playlist()) == 0) {
2546 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2551 begin_reversible_command (_("insert region"));
2552 playlist->clear_changes ();
2553 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2554 if (Config->get_edit_mode() == Ripple)
2555 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2557 _session->add_command(new StatefulDiffCommand (playlist));
2558 commit_reversible_command ();
2561 /* BUILT-IN EFFECTS */
2564 Editor::reverse_selection ()
2569 /* GAIN ENVELOPE EDITING */
2572 Editor::edit_envelope ()
2579 Editor::transition_to_rolling (bool fwd)
2585 if (_session->config.get_external_sync()) {
2586 switch (Config->get_sync_source()) {
2590 /* transport controlled by the master */
2595 if (_session->is_auditioning()) {
2596 _session->cancel_audition ();
2600 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2604 Editor::play_from_start ()
2606 _session->request_locate (_session->current_start_frame(), true);
2610 Editor::play_from_edit_point ()
2612 _session->request_locate (get_preferred_edit_position(), true);
2616 Editor::play_from_edit_point_and_return ()
2618 framepos_t start_frame;
2619 framepos_t return_frame;
2621 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2623 if (_session->transport_rolling()) {
2624 _session->request_locate (start_frame, false);
2628 /* don't reset the return frame if its already set */
2630 if ((return_frame = _session->requested_return_frame()) < 0) {
2631 return_frame = _session->audible_frame();
2634 if (start_frame >= 0) {
2635 _session->request_roll_at_and_return (start_frame, return_frame);
2640 Editor::play_selection ()
2642 framepos_t start, end;
2643 if (!get_selection_extents ( start, end))
2646 AudioRange ar (start, end, 0);
2647 list<AudioRange> lar;
2650 _session->request_play_range (&lar, true);
2655 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2657 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2660 location -= _session->preroll_samples (location);
2662 //don't try to locate before the beginning of time
2667 //if follow_playhead is on, keep the playhead on the screen
2668 if ( _follow_playhead )
2669 if ( location < leftmost_frame )
2670 location = leftmost_frame;
2672 _session->request_locate( location );
2676 Editor::play_with_preroll ()
2678 framepos_t start, end;
2679 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2680 const framepos_t preroll = _session->preroll_samples (start);
2682 framepos_t ret = start;
2684 if (start > preroll) {
2685 start = start - preroll;
2688 end = end + preroll; //"post-roll"
2690 AudioRange ar (start, end, 0);
2691 list<AudioRange> lar;
2694 _session->request_play_range (&lar, true);
2695 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2697 framepos_t ph = playhead_cursor->current_frame ();
2698 const framepos_t preroll = _session->preroll_samples (ph);
2701 start = ph - preroll;
2705 _session->request_locate (start, true);
2706 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2711 Editor::rec_with_preroll ()
2713 framepos_t ph = playhead_cursor->current_frame ();
2714 framepos_t preroll = _session->preroll_samples (ph);
2715 _session->request_preroll_record_trim (ph, preroll);
2719 Editor::rec_with_count_in ()
2721 _session->request_count_in_record ();
2725 Editor::play_location (Location& location)
2727 if (location.start() <= location.end()) {
2731 _session->request_bounded_roll (location.start(), location.end());
2735 Editor::loop_location (Location& location)
2737 if (location.start() <= location.end()) {
2743 if ((tll = transport_loop_location()) != 0) {
2744 tll->set (location.start(), location.end());
2746 // enable looping, reposition and start rolling
2747 _session->request_locate (tll->start(), true);
2748 _session->request_play_loop (true);
2753 Editor::do_layer_operation (LayerOperation op)
2755 if (selection->regions.empty ()) {
2759 bool const multiple = selection->regions.size() > 1;
2763 begin_reversible_command (_("raise regions"));
2765 begin_reversible_command (_("raise region"));
2771 begin_reversible_command (_("raise regions to top"));
2773 begin_reversible_command (_("raise region to top"));
2779 begin_reversible_command (_("lower regions"));
2781 begin_reversible_command (_("lower region"));
2787 begin_reversible_command (_("lower regions to bottom"));
2789 begin_reversible_command (_("lower region"));
2794 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2795 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2796 (*i)->clear_owned_changes ();
2799 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2800 boost::shared_ptr<Region> r = (*i)->region ();
2812 r->lower_to_bottom ();
2816 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2817 vector<Command*> cmds;
2819 _session->add_commands (cmds);
2822 commit_reversible_command ();
2826 Editor::raise_region ()
2828 do_layer_operation (Raise);
2832 Editor::raise_region_to_top ()
2834 do_layer_operation (RaiseToTop);
2838 Editor::lower_region ()
2840 do_layer_operation (Lower);
2844 Editor::lower_region_to_bottom ()
2846 do_layer_operation (LowerToBottom);
2849 /** Show the region editor for the selected regions */
2851 Editor::show_region_properties ()
2853 selection->foreach_regionview (&RegionView::show_region_editor);
2856 /** Show the midi list editor for the selected MIDI regions */
2858 Editor::show_midi_list_editor ()
2860 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2864 Editor::rename_region ()
2866 RegionSelection rs = get_regions_from_selection_and_entered ();
2872 ArdourDialog d (_("Rename Region"), true, false);
2874 Label label (_("New name:"));
2877 hbox.set_spacing (6);
2878 hbox.pack_start (label, false, false);
2879 hbox.pack_start (entry, true, true);
2881 d.get_vbox()->set_border_width (12);
2882 d.get_vbox()->pack_start (hbox, false, false);
2884 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2885 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2887 d.set_size_request (300, -1);
2889 entry.set_text (rs.front()->region()->name());
2890 entry.select_region (0, -1);
2892 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2898 int const ret = d.run();
2902 if (ret != RESPONSE_OK) {
2906 std::string str = entry.get_text();
2907 strip_whitespace_edges (str);
2909 rs.front()->region()->set_name (str);
2910 _regions->redisplay ();
2914 /** Start an audition of the first selected region */
2916 Editor::play_edit_range ()
2918 framepos_t start, end;
2920 if (get_edit_op_range (start, end)) {
2921 _session->request_bounded_roll (start, end);
2926 Editor::play_selected_region ()
2928 framepos_t start = max_framepos;
2931 RegionSelection rs = get_regions_from_selection_and_entered ();
2937 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2938 if ((*i)->region()->position() < start) {
2939 start = (*i)->region()->position();
2941 if ((*i)->region()->last_frame() + 1 > end) {
2942 end = (*i)->region()->last_frame() + 1;
2946 _session->request_bounded_roll (start, end);
2950 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2952 _session->audition_region (region);
2956 Editor::region_from_selection ()
2958 if (clicked_axisview == 0) {
2962 if (selection->time.empty()) {
2966 framepos_t start = selection->time[clicked_selection].start;
2967 framepos_t end = selection->time[clicked_selection].end;
2969 TrackViewList tracks = get_tracks_for_range_action ();
2971 framepos_t selection_cnt = end - start + 1;
2973 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2974 boost::shared_ptr<Region> current;
2975 boost::shared_ptr<Playlist> pl;
2976 framepos_t internal_start;
2979 if ((pl = (*i)->playlist()) == 0) {
2983 if ((current = pl->top_region_at (start)) == 0) {
2987 internal_start = start - current->position();
2988 RegionFactory::region_name (new_name, current->name(), true);
2992 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2993 plist.add (ARDOUR::Properties::length, selection_cnt);
2994 plist.add (ARDOUR::Properties::name, new_name);
2995 plist.add (ARDOUR::Properties::layer, 0);
2997 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
3002 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
3004 if (selection->time.empty() || selection->tracks.empty()) {
3008 framepos_t start, end;
3009 if (clicked_selection) {
3010 start = selection->time[clicked_selection].start;
3011 end = selection->time[clicked_selection].end;
3013 start = selection->time.start();
3014 end = selection->time.end_frame();
3017 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
3018 sort_track_selection (ts);
3020 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3021 boost::shared_ptr<Region> current;
3022 boost::shared_ptr<Playlist> playlist;
3023 framepos_t internal_start;
3026 if ((playlist = (*i)->playlist()) == 0) {
3030 if ((current = playlist->top_region_at(start)) == 0) {
3034 internal_start = start - current->position();
3035 RegionFactory::region_name (new_name, current->name(), true);
3039 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3040 plist.add (ARDOUR::Properties::length, end - start + 1);
3041 plist.add (ARDOUR::Properties::name, new_name);
3043 new_regions.push_back (RegionFactory::create (current, plist));
3048 Editor::split_multichannel_region ()
3050 RegionSelection rs = get_regions_from_selection_and_entered ();
3056 vector< boost::shared_ptr<Region> > v;
3058 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3059 (*x)->region()->separate_by_channel (v);
3064 Editor::new_region_from_selection ()
3066 region_from_selection ();
3067 cancel_selection ();
3071 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3073 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3074 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3075 case Evoral::OverlapNone:
3083 * - selected tracks, or if there are none...
3084 * - tracks containing selected regions, or if there are none...
3089 Editor::get_tracks_for_range_action () const
3093 if (selection->tracks.empty()) {
3095 /* use tracks with selected regions */
3097 RegionSelection rs = selection->regions;
3099 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3100 TimeAxisView* tv = &(*i)->get_time_axis_view();
3102 if (!t.contains (tv)) {
3108 /* no regions and no tracks: use all tracks */
3114 t = selection->tracks;
3117 return t.filter_to_unique_playlists();
3121 Editor::separate_regions_between (const TimeSelection& ts)
3123 bool in_command = false;
3124 boost::shared_ptr<Playlist> playlist;
3125 RegionSelection new_selection;
3127 TrackViewList tmptracks = get_tracks_for_range_action ();
3128 sort_track_selection (tmptracks);
3130 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3132 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3138 if (!rtv->is_track()) {
3142 /* no edits to destructive tracks */
3144 if (rtv->track()->destructive()) {
3148 if ((playlist = rtv->playlist()) != 0) {
3150 playlist->clear_changes ();
3152 /* XXX need to consider musical time selections here at some point */
3154 double speed = rtv->track()->speed();
3156 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3158 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3159 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3161 latest_regionviews.clear ();
3163 playlist->partition ((framepos_t)((*t).start * speed),
3164 (framepos_t)((*t).end * speed), false);
3168 if (!latest_regionviews.empty()) {
3170 rtv->view()->foreach_regionview (sigc::bind (
3171 sigc::ptr_fun (add_if_covered),
3172 &(*t), &new_selection));
3175 begin_reversible_command (_("separate"));
3179 /* pick up changes to existing regions */
3181 vector<Command*> cmds;
3182 playlist->rdiff (cmds);
3183 _session->add_commands (cmds);
3185 /* pick up changes to the playlist itself (adds/removes)
3188 _session->add_command(new StatefulDiffCommand (playlist));
3195 // selection->set (new_selection);
3197 commit_reversible_command ();
3201 struct PlaylistState {
3202 boost::shared_ptr<Playlist> playlist;
3206 /** Take tracks from get_tracks_for_range_action and cut any regions
3207 * on those tracks so that the tracks are empty over the time
3211 Editor::separate_region_from_selection ()
3213 /* preferentially use *all* ranges in the time selection if we're in range mode
3214 to allow discontiguous operation, since get_edit_op_range() currently
3215 returns a single range.
3218 if (!selection->time.empty()) {
3220 separate_regions_between (selection->time);
3227 if (get_edit_op_range (start, end)) {
3229 AudioRange ar (start, end, 1);
3233 separate_regions_between (ts);
3239 Editor::separate_region_from_punch ()
3241 Location* loc = _session->locations()->auto_punch_location();
3243 separate_regions_using_location (*loc);
3248 Editor::separate_region_from_loop ()
3250 Location* loc = _session->locations()->auto_loop_location();
3252 separate_regions_using_location (*loc);
3257 Editor::separate_regions_using_location (Location& loc)
3259 if (loc.is_mark()) {
3263 AudioRange ar (loc.start(), loc.end(), 1);
3268 separate_regions_between (ts);
3271 /** Separate regions under the selected region */
3273 Editor::separate_under_selected_regions ()
3275 vector<PlaylistState> playlists;
3279 rs = get_regions_from_selection_and_entered();
3281 if (!_session || rs.empty()) {
3285 begin_reversible_command (_("separate region under"));
3287 list<boost::shared_ptr<Region> > regions_to_remove;
3289 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3290 // we can't just remove the region(s) in this loop because
3291 // this removes them from the RegionSelection, and they thus
3292 // disappear from underneath the iterator, and the ++i above
3293 // SEGVs in a puzzling fashion.
3295 // so, first iterate over the regions to be removed from rs and
3296 // add them to the regions_to_remove list, and then
3297 // iterate over the list to actually remove them.
3299 regions_to_remove.push_back ((*i)->region());
3302 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3304 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3307 // is this check necessary?
3311 vector<PlaylistState>::iterator i;
3313 //only take state if this is a new playlist.
3314 for (i = playlists.begin(); i != playlists.end(); ++i) {
3315 if ((*i).playlist == playlist) {
3320 if (i == playlists.end()) {
3322 PlaylistState before;
3323 before.playlist = playlist;
3324 before.before = &playlist->get_state();
3325 playlist->clear_changes ();
3326 playlist->freeze ();
3327 playlists.push_back(before);
3330 //Partition on the region bounds
3331 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3333 //Re-add region that was just removed due to the partition operation
3334 playlist->add_region( (*rl), (*rl)->first_frame() );
3337 vector<PlaylistState>::iterator pl;
3339 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3340 (*pl).playlist->thaw ();
3341 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3344 commit_reversible_command ();
3348 Editor::crop_region_to_selection ()
3350 if (!selection->time.empty()) {
3352 begin_reversible_command (_("Crop Regions to Time Selection"));
3353 for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
3354 crop_region_to ((*i).start, (*i).end);
3356 commit_reversible_command();
3362 if (get_edit_op_range (start, end)) {
3363 begin_reversible_command (_("Crop Regions to Edit Range"));
3365 crop_region_to (start, end);
3367 commit_reversible_command();
3374 Editor::crop_region_to (framepos_t start, framepos_t end)
3376 vector<boost::shared_ptr<Playlist> > playlists;
3377 boost::shared_ptr<Playlist> playlist;
3380 if (selection->tracks.empty()) {
3381 ts = track_views.filter_to_unique_playlists();
3383 ts = selection->tracks.filter_to_unique_playlists ();
3386 sort_track_selection (ts);
3388 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3390 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3396 boost::shared_ptr<Track> t = rtv->track();
3398 if (t != 0 && ! t->destructive()) {
3400 if ((playlist = rtv->playlist()) != 0) {
3401 playlists.push_back (playlist);
3406 if (playlists.empty()) {
3411 framepos_t new_start;
3413 framecnt_t new_length;
3415 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3417 /* Only the top regions at start and end have to be cropped */
3418 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3419 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3421 vector<boost::shared_ptr<Region> > regions;
3423 if (region_at_start != 0) {
3424 regions.push_back (region_at_start);
3426 if (region_at_end != 0) {
3427 regions.push_back (region_at_end);
3430 /* now adjust lengths */
3431 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3433 pos = (*i)->position();
3434 new_start = max (start, pos);
3435 if (max_framepos - pos > (*i)->length()) {
3436 new_end = pos + (*i)->length() - 1;
3438 new_end = max_framepos;
3440 new_end = min (end, new_end);
3441 new_length = new_end - new_start + 1;
3443 (*i)->clear_changes ();
3444 (*i)->trim_to (new_start, new_length);
3445 _session->add_command (new StatefulDiffCommand (*i));
3451 Editor::region_fill_track ()
3453 boost::shared_ptr<Playlist> playlist;
3454 RegionSelection regions = get_regions_from_selection_and_entered ();
3455 RegionSelection foo;
3457 framepos_t const end = _session->current_end_frame ();
3459 if (regions.empty () || regions.end_frame () + 1 >= end) {
3463 framepos_t const start_frame = regions.start ();
3464 framepos_t const end_frame = regions.end_frame ();
3465 framecnt_t const gap = end_frame - start_frame + 1;
3467 begin_reversible_command (Operations::region_fill);
3469 selection->clear_regions ();
3471 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3473 boost::shared_ptr<Region> r ((*i)->region());
3475 TimeAxisView& tv = (*i)->get_time_axis_view();
3476 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3477 latest_regionviews.clear ();
3478 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3480 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3481 playlist = (*i)->region()->playlist();
3482 playlist->clear_changes ();
3483 playlist->duplicate_until (r, position, gap, end);
3484 _session->add_command(new StatefulDiffCommand (playlist));
3488 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3492 selection->set (foo);
3495 commit_reversible_command ();
3499 Editor::set_region_sync_position ()
3501 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3505 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3507 bool in_command = false;
3509 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3511 if (!(*r)->region()->covers (where)) {
3515 boost::shared_ptr<Region> region ((*r)->region());
3518 begin_reversible_command (_("set sync point"));
3522 region->clear_changes ();
3523 region->set_sync_position (where);
3524 _session->add_command(new StatefulDiffCommand (region));
3528 commit_reversible_command ();
3532 /** Remove the sync positions of the selection */
3534 Editor::remove_region_sync ()
3536 RegionSelection rs = get_regions_from_selection_and_entered ();
3542 begin_reversible_command (_("remove region sync"));
3544 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3546 (*i)->region()->clear_changes ();
3547 (*i)->region()->clear_sync_position ();
3548 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3551 commit_reversible_command ();
3555 Editor::naturalize_region ()
3557 RegionSelection rs = get_regions_from_selection_and_entered ();
3563 if (rs.size() > 1) {
3564 begin_reversible_command (_("move regions to original position"));
3566 begin_reversible_command (_("move region to original position"));
3569 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3570 (*i)->region()->clear_changes ();
3571 (*i)->region()->move_to_natural_position ();
3572 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3575 commit_reversible_command ();
3579 Editor::align_regions (RegionPoint what)
3581 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3587 begin_reversible_command (_("align selection"));
3589 framepos_t const position = get_preferred_edit_position ();
3591 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3592 align_region_internal ((*i)->region(), what, position);
3595 commit_reversible_command ();
3598 struct RegionSortByTime {
3599 bool operator() (const RegionView* a, const RegionView* b) {
3600 return a->region()->position() < b->region()->position();
3605 Editor::align_regions_relative (RegionPoint point)
3607 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3613 framepos_t const position = get_preferred_edit_position ();
3615 framepos_t distance = 0;
3619 list<RegionView*> sorted;
3620 rs.by_position (sorted);
3622 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3627 if (position > r->position()) {
3628 distance = position - r->position();
3630 distance = r->position() - position;
3636 if (position > r->last_frame()) {
3637 distance = position - r->last_frame();
3638 pos = r->position() + distance;
3640 distance = r->last_frame() - position;
3641 pos = r->position() - distance;
3647 pos = r->adjust_to_sync (position);
3648 if (pos > r->position()) {
3649 distance = pos - r->position();
3651 distance = r->position() - pos;
3657 if (pos == r->position()) {
3661 begin_reversible_command (_("align selection (relative)"));
3663 /* move first one specially */
3665 r->clear_changes ();
3666 r->set_position (pos);
3667 _session->add_command(new StatefulDiffCommand (r));
3669 /* move rest by the same amount */
3673 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3675 boost::shared_ptr<Region> region ((*i)->region());
3677 region->clear_changes ();
3680 region->set_position (region->position() + distance);
3682 region->set_position (region->position() - distance);
3685 _session->add_command(new StatefulDiffCommand (region));
3689 commit_reversible_command ();
3693 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3695 begin_reversible_command (_("align region"));
3696 align_region_internal (region, point, position);
3697 commit_reversible_command ();
3701 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3703 region->clear_changes ();
3707 region->set_position (region->adjust_to_sync (position));
3711 if (position > region->length()) {
3712 region->set_position (position - region->length());
3717 region->set_position (position);
3721 _session->add_command(new StatefulDiffCommand (region));
3725 Editor::trim_region_front ()
3731 Editor::trim_region_back ()
3733 trim_region (false);
3737 Editor::trim_region (bool front)
3739 framepos_t where = get_preferred_edit_position();
3740 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3746 begin_reversible_command (front ? _("trim front") : _("trim back"));
3748 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3749 if (!(*i)->region()->locked()) {
3751 (*i)->region()->clear_changes ();
3754 (*i)->region()->trim_front (where);
3756 (*i)->region()->trim_end (where);
3759 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3763 commit_reversible_command ();
3766 /** Trim the end of the selected regions to the position of the edit cursor */
3768 Editor::trim_region_to_loop ()
3770 Location* loc = _session->locations()->auto_loop_location();
3774 trim_region_to_location (*loc, _("trim to loop"));
3778 Editor::trim_region_to_punch ()
3780 Location* loc = _session->locations()->auto_punch_location();
3784 trim_region_to_location (*loc, _("trim to punch"));
3788 Editor::trim_region_to_location (const Location& loc, const char* str)
3790 RegionSelection rs = get_regions_from_selection_and_entered ();
3791 bool in_command = false;
3793 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3794 RegionView* rv = (*x);
3796 /* require region to span proposed trim */
3797 switch (rv->region()->coverage (loc.start(), loc.end())) {
3798 case Evoral::OverlapInternal:
3804 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3813 if (tav->track() != 0) {
3814 speed = tav->track()->speed();
3817 start = session_frame_to_track_frame (loc.start(), speed);
3818 end = session_frame_to_track_frame (loc.end(), speed);
3820 rv->region()->clear_changes ();
3821 rv->region()->trim_to (start, (end - start));
3824 begin_reversible_command (str);
3827 _session->add_command(new StatefulDiffCommand (rv->region()));
3831 commit_reversible_command ();
3836 Editor::trim_region_to_previous_region_end ()
3838 return trim_to_region(false);
3842 Editor::trim_region_to_next_region_start ()
3844 return trim_to_region(true);
3848 Editor::trim_to_region(bool forward)
3850 RegionSelection rs = get_regions_from_selection_and_entered ();
3851 bool in_command = false;
3853 boost::shared_ptr<Region> next_region;
3855 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3857 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3863 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3871 if (atav->track() != 0) {
3872 speed = atav->track()->speed();
3876 boost::shared_ptr<Region> region = arv->region();
3877 boost::shared_ptr<Playlist> playlist (region->playlist());
3879 region->clear_changes ();
3883 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3889 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3890 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3894 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3900 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3902 arv->region_changed (ARDOUR::bounds_change);
3906 begin_reversible_command (_("trim to region"));
3909 _session->add_command(new StatefulDiffCommand (region));
3913 commit_reversible_command ();
3918 Editor::unfreeze_route ()
3920 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3924 clicked_routeview->track()->unfreeze ();
3928 Editor::_freeze_thread (void* arg)
3930 return static_cast<Editor*>(arg)->freeze_thread ();
3934 Editor::freeze_thread ()
3936 /* create event pool because we may need to talk to the session */
3937 SessionEvent::create_per_thread_pool ("freeze events", 64);
3938 /* create per-thread buffers for process() tree to use */
3939 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3940 current_interthread_info->done = true;
3945 Editor::freeze_route ()
3951 /* stop transport before we start. this is important */
3953 _session->request_transport_speed (0.0);
3955 /* wait for just a little while, because the above call is asynchronous */
3957 Glib::usleep (250000);
3959 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3963 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3965 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3966 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3968 d.set_title (_("Cannot freeze"));
3973 if (clicked_routeview->track()->has_external_redirects()) {
3974 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"
3975 "Freezing will only process the signal as far as the first send/insert/return."),
3976 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3978 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3979 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3980 d.set_title (_("Freeze Limits"));
3982 int response = d.run ();
3985 case Gtk::RESPONSE_CANCEL:
3992 InterThreadInfo itt;
3993 current_interthread_info = &itt;
3995 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3997 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3999 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
4001 while (!itt.done && !itt.cancel) {
4002 gtk_main_iteration ();
4005 pthread_join (itt.thread, 0);
4006 current_interthread_info = 0;
4010 Editor::bounce_range_selection (bool replace, bool enable_processing)
4012 if (selection->time.empty()) {
4016 TrackSelection views = selection->tracks;
4018 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4020 if (enable_processing) {
4022 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4024 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4026 _("You can't perform this operation because the processing of the signal "
4027 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4028 "You can do this without processing, which is a different operation.")
4030 d.set_title (_("Cannot bounce"));
4037 framepos_t start = selection->time[clicked_selection].start;
4038 framepos_t end = selection->time[clicked_selection].end;
4039 framepos_t cnt = end - start + 1;
4040 bool in_command = false;
4042 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4044 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4050 boost::shared_ptr<Playlist> playlist;
4052 if ((playlist = rtv->playlist()) == 0) {
4056 InterThreadInfo itt;
4058 playlist->clear_changes ();
4059 playlist->clear_owned_changes ();
4061 boost::shared_ptr<Region> r;
4063 if (enable_processing) {
4064 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4066 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4074 list<AudioRange> ranges;
4075 ranges.push_back (AudioRange (start, start+cnt, 0));
4076 playlist->cut (ranges); // discard result
4077 playlist->add_region (r, start);
4081 begin_reversible_command (_("bounce range"));
4084 vector<Command*> cmds;
4085 playlist->rdiff (cmds);
4086 _session->add_commands (cmds);
4088 _session->add_command (new StatefulDiffCommand (playlist));
4092 commit_reversible_command ();
4096 /** Delete selected regions, automation points or a time range */
4100 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4101 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4102 bool deleted = false;
4103 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4104 deleted = current_mixer_strip->delete_processors ();
4110 /** Cut selected regions, automation points or a time range */
4117 /** Copy selected regions, automation points or a time range */
4125 /** @return true if a Cut, Copy or Clear is possible */
4127 Editor::can_cut_copy () const
4129 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4136 /** Cut, copy or clear selected regions, automation points or a time range.
4137 * @param op Operation (Delete, Cut, Copy or Clear)
4140 Editor::cut_copy (CutCopyOp op)
4142 /* only cancel selection if cut/copy is successful.*/
4148 opname = _("delete");
4157 opname = _("clear");
4161 /* if we're deleting something, and the mouse is still pressed,
4162 the thing we started a drag for will be gone when we release
4163 the mouse button(s). avoid this. see part 2 at the end of
4167 if (op == Delete || op == Cut || op == Clear) {
4168 if (_drags->active ()) {
4173 if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
4174 cut_buffer->clear ();
4177 if (entered_marker) {
4179 /* cut/delete op while pointing at a marker */
4182 Location* loc = find_location_from_marker (entered_marker, ignored);
4184 if (_session && loc) {
4185 entered_marker = NULL;
4186 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4193 switch (mouse_mode) {
4196 begin_reversible_command (opname + ' ' + X_("MIDI"));
4198 commit_reversible_command ();
4204 bool did_edit = false;
4206 if (!selection->regions.empty() || !selection->points.empty()) {
4207 begin_reversible_command (opname + ' ' + _("objects"));
4210 if (!selection->regions.empty()) {
4211 cut_copy_regions (op, selection->regions);
4213 if (op == Cut || op == Delete) {
4214 selection->clear_regions ();
4218 if (!selection->points.empty()) {
4219 cut_copy_points (op);
4221 if (op == Cut || op == Delete) {
4222 selection->clear_points ();
4225 } else if (selection->time.empty()) {
4226 framepos_t start, end;
4227 /* no time selection, see if we can get an edit range
4230 if (get_edit_op_range (start, end)) {
4231 selection->set (start, end);
4233 } else if (!selection->time.empty()) {
4234 begin_reversible_command (opname + ' ' + _("range"));
4237 cut_copy_ranges (op);
4239 if (op == Cut || op == Delete) {
4240 selection->clear_time ();
4245 /* reset repeated paste state */
4247 last_paste_pos = -1;
4248 commit_reversible_command ();
4251 if (op == Delete || op == Cut || op == Clear) {
4257 struct AutomationRecord {
4258 AutomationRecord () : state (0) , line(NULL) {}
4259 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4261 XMLNode* state; ///< state before any operation
4262 const AutomationLine* line; ///< line this came from
4263 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4266 struct PointsSelectionPositionSorter {
4267 bool operator() (ControlPoint* a, ControlPoint* b) {
4268 return (*(a->model()))->when < (*(b->model()))->when;
4272 /** Cut, copy or clear selected automation points.
4273 * @param op Operation (Cut, Copy or Clear)
4276 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4278 if (selection->points.empty ()) {
4282 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4283 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4285 /* Keep a record of the AutomationLists that we end up using in this operation */
4286 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4289 /* user could select points in any order */
4290 selection->points.sort(PointsSelectionPositionSorter ());
4292 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4293 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4294 const AutomationLine& line = (*sel_point)->line();
4295 const boost::shared_ptr<AutomationList> al = line.the_list();
4296 if (lists.find (al) == lists.end ()) {
4297 /* We haven't seen this list yet, so make a record for it. This includes
4298 taking a copy of its current state, in case this is needed for undo later.
4300 lists[al] = AutomationRecord (&al->get_state (), &line);
4304 if (op == Cut || op == Copy) {
4305 /* This operation will involve putting things in the cut buffer, so create an empty
4306 ControlList for each of our source lists to put the cut buffer data in.
4308 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4309 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4312 /* Add all selected points to the relevant copy ControlLists */
4313 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4314 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4315 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4316 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4318 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4320 /* Update earliest MIDI start time in beats */
4321 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4323 /* Update earliest session start time in frames */
4324 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4328 /* Snap start time backwards, so copy/paste is snap aligned. */
4330 if (earliest == Evoral::Beats::max()) {
4331 earliest = Evoral::Beats(); // Weird... don't offset
4333 earliest.round_down_to_beat();
4335 if (start.frame == std::numeric_limits<double>::max()) {
4336 start.frame = 0; // Weird... don't offset
4338 snap_to(start, RoundDownMaybe);
4341 const double line_offset = midi ? earliest.to_double() : start.frame;
4342 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4343 /* Correct this copy list so that it is relative to the earliest
4344 start time, so relative ordering between points is preserved
4345 when copying from several lists and the paste starts at the
4346 earliest copied piece of data. */
4347 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4348 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4349 (*ctrl_evt)->when -= line_offset;
4352 /* And add it to the cut buffer */
4353 cut_buffer->add (al_cpy);
4357 if (op == Delete || op == Cut) {
4358 /* This operation needs to remove things from the main AutomationList, so do that now */
4360 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4361 i->first->freeze ();
4364 /* Remove each selected point from its AutomationList */
4365 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4366 AutomationLine& line = (*sel_point)->line ();
4367 boost::shared_ptr<AutomationList> al = line.the_list();
4371 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4372 /* removing of first and last gain point in region gain lines is prohibited*/
4373 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4379 al->erase ((*sel_point)->model ());
4383 /* Thaw the lists and add undo records for them */
4384 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4385 boost::shared_ptr<AutomationList> al = i->first;
4387 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4392 /** Cut, copy or clear selected automation points.
4393 * @param op Operation (Cut, Copy or Clear)
4396 Editor::cut_copy_midi (CutCopyOp op)
4398 Evoral::Beats earliest = Evoral::Beats::max();
4399 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4400 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4402 if (!mrv->selection().empty()) {
4403 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4405 mrv->cut_copy_clear (op);
4407 /* XXX: not ideal, as there may be more than one track involved in the selection */
4408 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4412 if (!selection->points.empty()) {
4413 cut_copy_points (op, earliest, true);
4414 if (op == Cut || op == Delete) {
4415 selection->clear_points ();
4420 struct lt_playlist {
4421 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4422 return a.playlist < b.playlist;
4426 struct PlaylistMapping {
4428 boost::shared_ptr<Playlist> pl;
4430 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4433 /** Remove `clicked_regionview' */
4435 Editor::remove_clicked_region ()
4437 if (clicked_routeview == 0 || clicked_regionview == 0) {
4441 begin_reversible_command (_("remove region"));
4443 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4445 playlist->clear_changes ();
4446 playlist->clear_owned_changes ();
4447 playlist->remove_region (clicked_regionview->region());
4448 if (Config->get_edit_mode() == Ripple)
4449 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4451 /* We might have removed regions, which alters other regions' layering_index,
4452 so we need to do a recursive diff here.
4454 vector<Command*> cmds;
4455 playlist->rdiff (cmds);
4456 _session->add_commands (cmds);
4458 _session->add_command(new StatefulDiffCommand (playlist));
4459 commit_reversible_command ();
4463 /** Remove the selected regions */
4465 Editor::remove_selected_regions ()
4467 RegionSelection rs = get_regions_from_selection_and_entered ();
4469 if (!_session || rs.empty()) {
4473 list<boost::shared_ptr<Region> > regions_to_remove;
4475 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4476 // we can't just remove the region(s) in this loop because
4477 // this removes them from the RegionSelection, and they thus
4478 // disappear from underneath the iterator, and the ++i above
4479 // SEGVs in a puzzling fashion.
4481 // so, first iterate over the regions to be removed from rs and
4482 // add them to the regions_to_remove list, and then
4483 // iterate over the list to actually remove them.
4485 regions_to_remove.push_back ((*i)->region());
4488 vector<boost::shared_ptr<Playlist> > playlists;
4490 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4492 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4495 // is this check necessary?
4499 /* get_regions_from_selection_and_entered() guarantees that
4500 the playlists involved are unique, so there is no need
4504 playlists.push_back (playlist);
4506 playlist->clear_changes ();
4507 playlist->clear_owned_changes ();
4508 playlist->freeze ();
4509 playlist->remove_region (*rl);
4510 if (Config->get_edit_mode() == Ripple)
4511 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4515 vector<boost::shared_ptr<Playlist> >::iterator pl;
4516 bool in_command = false;
4518 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4521 /* We might have removed regions, which alters other regions' layering_index,
4522 so we need to do a recursive diff here.
4526 begin_reversible_command (_("remove region"));
4529 vector<Command*> cmds;
4530 (*pl)->rdiff (cmds);
4531 _session->add_commands (cmds);
4533 _session->add_command(new StatefulDiffCommand (*pl));
4537 commit_reversible_command ();
4541 /** Cut, copy or clear selected regions.
4542 * @param op Operation (Cut, Copy or Clear)
4545 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4547 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4548 a map when we want ordered access to both elements. i think.
4551 vector<PlaylistMapping> pmap;
4553 framepos_t first_position = max_framepos;
4555 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4556 FreezeList freezelist;
4558 /* get ordering correct before we cut/copy */
4560 rs.sort_by_position_and_track ();
4562 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4564 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4566 if (op == Cut || op == Clear || op == Delete) {
4567 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4570 FreezeList::iterator fl;
4572 // only take state if this is a new playlist.
4573 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4579 if (fl == freezelist.end()) {
4580 pl->clear_changes();
4581 pl->clear_owned_changes ();
4583 freezelist.insert (pl);
4588 TimeAxisView* tv = &(*x)->get_time_axis_view();
4589 vector<PlaylistMapping>::iterator z;
4591 for (z = pmap.begin(); z != pmap.end(); ++z) {
4592 if ((*z).tv == tv) {
4597 if (z == pmap.end()) {
4598 pmap.push_back (PlaylistMapping (tv));
4602 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4604 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4607 /* region not yet associated with a playlist (e.g. unfinished
4614 TimeAxisView& tv = (*x)->get_time_axis_view();
4615 boost::shared_ptr<Playlist> npl;
4616 RegionSelection::iterator tmp;
4623 vector<PlaylistMapping>::iterator z;
4625 for (z = pmap.begin(); z != pmap.end(); ++z) {
4626 if ((*z).tv == &tv) {
4631 assert (z != pmap.end());
4634 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4642 boost::shared_ptr<Region> r = (*x)->region();
4643 boost::shared_ptr<Region> _xx;
4649 pl->remove_region (r);
4650 if (Config->get_edit_mode() == Ripple)
4651 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4655 _xx = RegionFactory::create (r);
4656 npl->add_region (_xx, r->position() - first_position);
4657 pl->remove_region (r);
4658 if (Config->get_edit_mode() == Ripple)
4659 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4663 /* copy region before adding, so we're not putting same object into two different playlists */
4664 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4668 pl->remove_region (r);
4669 if (Config->get_edit_mode() == Ripple)
4670 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4679 list<boost::shared_ptr<Playlist> > foo;
4681 /* the pmap is in the same order as the tracks in which selected regions occurred */
4683 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4686 foo.push_back ((*i).pl);
4691 cut_buffer->set (foo);
4695 _last_cut_copy_source_track = 0;
4697 _last_cut_copy_source_track = pmap.front().tv;
4701 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4704 /* We might have removed regions, which alters other regions' layering_index,
4705 so we need to do a recursive diff here.
4707 vector<Command*> cmds;
4708 (*pl)->rdiff (cmds);
4709 _session->add_commands (cmds);
4711 _session->add_command (new StatefulDiffCommand (*pl));
4716 Editor::cut_copy_ranges (CutCopyOp op)
4718 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4720 /* Sort the track selection now, so that it if is used, the playlists
4721 selected by the calls below to cut_copy_clear are in the order that
4722 their tracks appear in the editor. This makes things like paste
4723 of ranges work properly.
4726 sort_track_selection (ts);
4729 if (!entered_track) {
4732 ts.push_back (entered_track);
4735 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4736 (*i)->cut_copy_clear (*selection, op);
4741 Editor::paste (float times, bool from_context)
4743 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4744 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4745 paste_internal (where.frame, times, 0);
4749 Editor::mouse_paste ()
4751 MusicFrame where (0, 0);
4753 if (!mouse_frame (where.frame, ignored)) {
4758 paste_internal (where.frame, 1, where.division);
4762 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4764 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4766 if (cut_buffer->empty(internal_editing())) {
4770 if (position == max_framepos) {
4771 position = get_preferred_edit_position();
4772 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4775 if (position == last_paste_pos) {
4776 /* repeated paste in the same position */
4779 /* paste in new location, reset repeated paste state */
4781 last_paste_pos = position;
4784 /* get everything in the correct order */
4787 if (!selection->tracks.empty()) {
4788 /* If there is a track selection, paste into exactly those tracks and
4789 * only those tracks. This allows the user to be explicit and override
4790 * the below "do the reasonable thing" logic. */
4791 ts = selection->tracks.filter_to_unique_playlists ();
4792 sort_track_selection (ts);
4794 /* Figure out which track to base the paste at. */
4795 TimeAxisView* base_track = NULL;
4796 if (_edit_point == Editing::EditAtMouse && entered_track) {
4797 /* With the mouse edit point, paste onto the track under the mouse. */
4798 base_track = entered_track;
4799 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4800 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4801 base_track = &entered_regionview->get_time_axis_view();
4802 } else if (_last_cut_copy_source_track) {
4803 /* Paste to the track that the cut/copy came from (see mantis #333). */
4804 base_track = _last_cut_copy_source_track;
4806 /* This is "impossible" since we've copied... well, do nothing. */
4810 /* Walk up to parent if necessary, so base track is a route. */
4811 while (base_track->get_parent()) {
4812 base_track = base_track->get_parent();
4815 /* Add base track and all tracks below it. The paste logic will select
4816 the appropriate object types from the cut buffer in relative order. */
4817 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4818 if ((*i)->order() >= base_track->order()) {
4823 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4824 sort_track_selection (ts);
4826 /* Add automation children of each track in order, for pasting several lines. */
4827 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4828 /* Add any automation children for pasting several lines */
4829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4834 typedef RouteTimeAxisView::AutomationTracks ATracks;
4835 const ATracks& atracks = rtv->automation_tracks();
4836 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4837 i = ts.insert(i, a->second.get());
4842 /* We now have a list of trackviews starting at base_track, including
4843 automation children, in the order shown in the editor, e.g. R1,
4844 R1.A1, R1.A2, R2, R2.A1, ... */
4847 begin_reversible_command (Operations::paste);
4849 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4850 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4851 /* Only one line copied, and one automation track selected. Do a
4852 "greedy" paste from one automation type to another. */
4854 PasteContext ctx(paste_count, times, ItemCounts(), true);
4855 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4859 /* Paste into tracks */
4861 PasteContext ctx(paste_count, times, ItemCounts(), false);
4862 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4863 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4867 commit_reversible_command ();
4871 Editor::duplicate_regions (float times)
4873 RegionSelection rs (get_regions_from_selection_and_entered());
4874 duplicate_some_regions (rs, times);
4878 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4880 if (regions.empty ()) {
4884 boost::shared_ptr<Playlist> playlist;
4885 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4886 RegionSelection foo;
4888 framepos_t const start_frame = regions.start ();
4889 framepos_t const end_frame = regions.end_frame ();
4890 framecnt_t const gap = end_frame - start_frame + 1;
4892 begin_reversible_command (Operations::duplicate_region);
4894 selection->clear_regions ();
4896 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4898 boost::shared_ptr<Region> r ((*i)->region());
4900 TimeAxisView& tv = (*i)->get_time_axis_view();
4901 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4902 latest_regionviews.clear ();
4903 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4905 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4906 playlist = (*i)->region()->playlist();
4907 playlist->clear_changes ();
4908 playlist->duplicate (r, position, gap, times);
4909 _session->add_command(new StatefulDiffCommand (playlist));
4913 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4917 selection->set (foo);
4920 commit_reversible_command ();
4924 Editor::duplicate_selection (float times)
4926 if (selection->time.empty() || selection->tracks.empty()) {
4930 boost::shared_ptr<Playlist> playlist;
4932 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4934 bool in_command = false;
4936 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4937 if ((playlist = (*i)->playlist()) == 0) {
4940 playlist->clear_changes ();
4942 if (clicked_selection) {
4943 playlist->duplicate_range (selection->time[clicked_selection], times);
4945 playlist->duplicate_ranges (selection->time, times);
4949 begin_reversible_command (_("duplicate range selection"));
4952 _session->add_command (new StatefulDiffCommand (playlist));
4957 if (times == 1.0f) {
4958 // now "move" range selection to after the current range selection
4959 framecnt_t distance = 0;
4961 if (clicked_selection) {
4963 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4965 distance = selection->time.end_frame () - selection->time.start ();
4968 selection->move_time (distance);
4970 commit_reversible_command ();
4974 /** Reset all selected points to the relevant default value */
4976 Editor::reset_point_selection ()
4978 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4979 ARDOUR::AutomationList::iterator j = (*i)->model ();
4980 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4985 Editor::center_playhead ()
4987 float const page = _visible_canvas_width * samples_per_pixel;
4988 center_screen_internal (playhead_cursor->current_frame (), page);
4992 Editor::center_edit_point ()
4994 float const page = _visible_canvas_width * samples_per_pixel;
4995 center_screen_internal (get_preferred_edit_position(), page);
4998 /** Caller must begin and commit a reversible command */
5000 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
5002 playlist->clear_changes ();
5004 _session->add_command (new StatefulDiffCommand (playlist));
5008 Editor::nudge_track (bool use_edit, bool forwards)
5010 boost::shared_ptr<Playlist> playlist;
5011 framepos_t distance;
5012 framepos_t next_distance;
5016 start = get_preferred_edit_position();
5021 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5025 if (selection->tracks.empty()) {
5029 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5030 bool in_command = false;
5032 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5034 if ((playlist = (*i)->playlist()) == 0) {
5038 playlist->clear_changes ();
5039 playlist->clear_owned_changes ();
5041 playlist->nudge_after (start, distance, forwards);
5044 begin_reversible_command (_("nudge track"));
5047 vector<Command*> cmds;
5049 playlist->rdiff (cmds);
5050 _session->add_commands (cmds);
5052 _session->add_command (new StatefulDiffCommand (playlist));
5056 commit_reversible_command ();
5061 Editor::remove_last_capture ()
5063 vector<string> choices;
5070 if (Config->get_verify_remove_last_capture()) {
5071 prompt = _("Do you really want to destroy the last capture?"
5072 "\n(This is destructive and cannot be undone)");
5074 choices.push_back (_("No, do nothing."));
5075 choices.push_back (_("Yes, destroy it."));
5077 Choice prompter (_("Destroy last capture"), prompt, choices);
5079 if (prompter.run () == 1) {
5080 _session->remove_last_capture ();
5081 _regions->redisplay ();
5085 _session->remove_last_capture();
5086 _regions->redisplay ();
5091 Editor::normalize_region ()
5097 RegionSelection rs = get_regions_from_selection_and_entered ();
5103 NormalizeDialog dialog (rs.size() > 1);
5105 if (dialog.run () != RESPONSE_ACCEPT) {
5109 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5112 /* XXX: should really only count audio regions here */
5113 int const regions = rs.size ();
5115 /* Make a list of the selected audio regions' maximum amplitudes, and also
5116 obtain the maximum amplitude of them all.
5118 list<double> max_amps;
5119 list<double> rms_vals;
5122 bool use_rms = dialog.constrain_rms ();
5124 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5125 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5129 dialog.descend (1.0 / regions);
5130 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5132 double r = arv->audio_region()->rms (&dialog);
5133 max_rms = max (max_rms, r);
5134 rms_vals.push_back (r);
5138 /* the user cancelled the operation */
5142 max_amps.push_back (a);
5143 max_amp = max (max_amp, a);
5147 list<double>::const_iterator a = max_amps.begin ();
5148 list<double>::const_iterator l = rms_vals.begin ();
5149 bool in_command = false;
5151 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5152 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5157 arv->region()->clear_changes ();
5159 double amp = dialog.normalize_individually() ? *a : max_amp;
5160 double target = dialog.target_peak (); // dB
5163 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5164 const double t_rms = dialog.target_rms ();
5165 const gain_t c_peak = dB_to_coefficient (target);
5166 const gain_t c_rms = dB_to_coefficient (t_rms);
5167 if ((amp_rms / c_rms) > (amp / c_peak)) {
5173 arv->audio_region()->normalize (amp, target);
5176 begin_reversible_command (_("normalize"));
5179 _session->add_command (new StatefulDiffCommand (arv->region()));
5186 commit_reversible_command ();
5192 Editor::reset_region_scale_amplitude ()
5198 RegionSelection rs = get_regions_from_selection_and_entered ();
5204 bool in_command = false;
5206 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5207 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5210 arv->region()->clear_changes ();
5211 arv->audio_region()->set_scale_amplitude (1.0f);
5214 begin_reversible_command ("reset gain");
5217 _session->add_command (new StatefulDiffCommand (arv->region()));
5221 commit_reversible_command ();
5226 Editor::adjust_region_gain (bool up)
5228 RegionSelection rs = get_regions_from_selection_and_entered ();
5230 if (!_session || rs.empty()) {
5234 bool in_command = false;
5236 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5237 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5242 arv->region()->clear_changes ();
5244 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5252 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5255 begin_reversible_command ("adjust region gain");
5258 _session->add_command (new StatefulDiffCommand (arv->region()));
5262 commit_reversible_command ();
5267 Editor::reset_region_gain ()
5269 RegionSelection rs = get_regions_from_selection_and_entered ();
5271 if (!_session || rs.empty()) {
5275 bool in_command = false;
5277 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5278 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5283 arv->region()->clear_changes ();
5285 arv->audio_region()->set_scale_amplitude (1.0f);
5288 begin_reversible_command ("reset region gain");
5291 _session->add_command (new StatefulDiffCommand (arv->region()));
5295 commit_reversible_command ();
5300 Editor::reverse_region ()
5306 Reverse rev (*_session);
5307 apply_filter (rev, _("reverse regions"));
5311 Editor::strip_region_silence ()
5317 RegionSelection rs = get_regions_from_selection_and_entered ();
5323 std::list<RegionView*> audio_only;
5325 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5326 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5328 audio_only.push_back (arv);
5332 assert (!audio_only.empty());
5334 StripSilenceDialog d (_session, audio_only);
5335 int const r = d.run ();
5339 if (r == Gtk::RESPONSE_OK) {
5340 ARDOUR::AudioIntervalMap silences;
5341 d.silences (silences);
5342 StripSilence s (*_session, silences, d.fade_length());
5344 apply_filter (s, _("strip silence"), &d);
5349 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5351 Evoral::Sequence<Evoral::Beats>::Notes selected;
5352 mrv.selection_as_notelist (selected, true);
5354 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5355 v.push_back (selected);
5357 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5359 return op (mrv.midi_region()->model(), pos_beats, v);
5363 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5369 bool in_command = false;
5371 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5372 RegionSelection::const_iterator tmp = r;
5375 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5378 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5381 begin_reversible_command (op.name ());
5385 _session->add_command (cmd);
5393 commit_reversible_command ();
5394 _session->set_dirty ();
5399 Editor::fork_region ()
5401 RegionSelection rs = get_regions_from_selection_and_entered ();
5407 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5408 bool in_command = false;
5412 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5413 RegionSelection::iterator tmp = r;
5416 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5420 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5421 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5422 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5425 begin_reversible_command (_("Fork Region(s)"));
5428 playlist->clear_changes ();
5429 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5430 _session->add_command(new StatefulDiffCommand (playlist));
5432 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5440 commit_reversible_command ();
5445 Editor::quantize_region ()
5448 quantize_regions(get_regions_from_selection_and_entered ());
5453 Editor::quantize_regions (const RegionSelection& rs)
5455 if (rs.n_midi_regions() == 0) {
5459 if (!quantize_dialog) {
5460 quantize_dialog = new QuantizeDialog (*this);
5463 if (quantize_dialog->is_mapped()) {
5464 /* in progress already */
5468 quantize_dialog->present ();
5469 const int r = quantize_dialog->run ();
5470 quantize_dialog->hide ();
5472 if (r == Gtk::RESPONSE_OK) {
5473 Quantize quant (quantize_dialog->snap_start(),
5474 quantize_dialog->snap_end(),
5475 quantize_dialog->start_grid_size(),
5476 quantize_dialog->end_grid_size(),
5477 quantize_dialog->strength(),
5478 quantize_dialog->swing(),
5479 quantize_dialog->threshold());
5481 apply_midi_note_edit_op (quant, rs);
5486 Editor::legatize_region (bool shrink_only)
5489 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5494 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5496 if (rs.n_midi_regions() == 0) {
5500 Legatize legatize(shrink_only);
5501 apply_midi_note_edit_op (legatize, rs);
5505 Editor::transform_region ()
5508 transform_regions(get_regions_from_selection_and_entered ());
5513 Editor::transform_regions (const RegionSelection& rs)
5515 if (rs.n_midi_regions() == 0) {
5522 const int r = td.run();
5525 if (r == Gtk::RESPONSE_OK) {
5526 Transform transform(td.get());
5527 apply_midi_note_edit_op(transform, rs);
5532 Editor::transpose_region ()
5535 transpose_regions(get_regions_from_selection_and_entered ());
5540 Editor::transpose_regions (const RegionSelection& rs)
5542 if (rs.n_midi_regions() == 0) {
5547 int const r = d.run ();
5549 if (r == RESPONSE_ACCEPT) {
5550 Transpose transpose(d.semitones ());
5551 apply_midi_note_edit_op (transpose, rs);
5556 Editor::insert_patch_change (bool from_context)
5558 RegionSelection rs = get_regions_from_selection_and_entered ();
5564 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5566 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5567 there may be more than one, but the PatchChangeDialog can only offer
5568 one set of patch menus.
5570 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5572 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5573 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5575 if (d.run() == RESPONSE_CANCEL) {
5579 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5580 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5582 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5583 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5590 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5592 RegionSelection rs = get_regions_from_selection_and_entered ();
5598 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5599 bool in_command = false;
5604 int const N = rs.size ();
5606 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5607 RegionSelection::iterator tmp = r;
5610 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5612 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5615 progress->descend (1.0 / N);
5618 if (arv->audio_region()->apply (filter, progress) == 0) {
5620 playlist->clear_changes ();
5621 playlist->clear_owned_changes ();
5624 begin_reversible_command (command);
5628 if (filter.results.empty ()) {
5630 /* no regions returned; remove the old one */
5631 playlist->remove_region (arv->region ());
5635 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5637 /* first region replaces the old one */
5638 playlist->replace_region (arv->region(), *res, (*res)->position());
5642 while (res != filter.results.end()) {
5643 playlist->add_region (*res, (*res)->position());
5649 /* We might have removed regions, which alters other regions' layering_index,
5650 so we need to do a recursive diff here.
5652 vector<Command*> cmds;
5653 playlist->rdiff (cmds);
5654 _session->add_commands (cmds);
5656 _session->add_command(new StatefulDiffCommand (playlist));
5660 progress->ascend ();
5669 commit_reversible_command ();
5674 Editor::external_edit_region ()
5680 Editor::reset_region_gain_envelopes ()
5682 RegionSelection rs = get_regions_from_selection_and_entered ();
5684 if (!_session || rs.empty()) {
5688 bool in_command = false;
5690 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5691 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5693 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5694 XMLNode& before (alist->get_state());
5696 arv->audio_region()->set_default_envelope ();
5699 begin_reversible_command (_("reset region gain"));
5702 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5707 commit_reversible_command ();
5712 Editor::set_region_gain_visibility (RegionView* rv)
5714 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5716 arv->update_envelope_visibility();
5721 Editor::set_gain_envelope_visibility ()
5727 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5728 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5730 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5736 Editor::toggle_gain_envelope_active ()
5738 if (_ignore_region_action) {
5742 RegionSelection rs = get_regions_from_selection_and_entered ();
5744 if (!_session || rs.empty()) {
5748 bool in_command = false;
5750 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5751 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5753 arv->region()->clear_changes ();
5754 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5757 begin_reversible_command (_("region gain envelope active"));
5760 _session->add_command (new StatefulDiffCommand (arv->region()));
5765 commit_reversible_command ();
5770 Editor::toggle_region_lock ()
5772 if (_ignore_region_action) {
5776 RegionSelection rs = get_regions_from_selection_and_entered ();
5778 if (!_session || rs.empty()) {
5782 begin_reversible_command (_("toggle region lock"));
5784 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5785 (*i)->region()->clear_changes ();
5786 (*i)->region()->set_locked (!(*i)->region()->locked());
5787 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5790 commit_reversible_command ();
5794 Editor::toggle_region_video_lock ()
5796 if (_ignore_region_action) {
5800 RegionSelection rs = get_regions_from_selection_and_entered ();
5802 if (!_session || rs.empty()) {
5806 begin_reversible_command (_("Toggle Video Lock"));
5808 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5809 (*i)->region()->clear_changes ();
5810 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5811 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5814 commit_reversible_command ();
5818 Editor::toggle_region_lock_style ()
5820 if (_ignore_region_action) {
5824 RegionSelection rs = get_regions_from_selection_and_entered ();
5826 if (!_session || rs.empty()) {
5830 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5831 vector<Widget*> proxies = a->get_proxies();
5832 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5836 begin_reversible_command (_("toggle region lock style"));
5838 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5839 (*i)->region()->clear_changes ();
5840 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5841 (*i)->region()->set_position_lock_style (ns);
5842 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5845 commit_reversible_command ();
5849 Editor::toggle_opaque_region ()
5851 if (_ignore_region_action) {
5855 RegionSelection rs = get_regions_from_selection_and_entered ();
5857 if (!_session || rs.empty()) {
5861 begin_reversible_command (_("change region opacity"));
5863 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5864 (*i)->region()->clear_changes ();
5865 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5866 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5869 commit_reversible_command ();
5873 Editor::toggle_record_enable ()
5875 bool new_state = false;
5877 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5878 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5881 if (!rtav->is_track())
5885 new_state = !rtav->track()->rec_enable_control()->get_value();
5889 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5894 Editor::toggle_solo ()
5896 bool new_state = false;
5898 boost::shared_ptr<ControlList> cl (new ControlList);
5900 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5901 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5903 if (!stav || !stav->stripable()->solo_control()) {
5908 new_state = !stav->stripable()->solo_control()->soloed ();
5912 cl->push_back (stav->stripable()->solo_control());
5915 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5919 Editor::toggle_mute ()
5921 bool new_state = false;
5923 boost::shared_ptr<ControlList> cl (new ControlList);
5925 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5926 StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
5928 if (!stav || !stav->stripable()->mute_control()) {
5933 new_state = !stav->stripable()->mute_control()->muted();
5937 cl->push_back (stav->stripable()->mute_control());
5940 _session->set_controls (cl, new_state, Controllable::UseGroup);
5944 Editor::toggle_solo_isolate ()
5950 Editor::fade_range ()
5952 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5954 begin_reversible_command (_("fade range"));
5956 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5957 (*i)->fade_range (selection->time);
5960 commit_reversible_command ();
5965 Editor::set_fade_length (bool in)
5967 RegionSelection rs = get_regions_from_selection_and_entered ();
5973 /* we need a region to measure the offset from the start */
5975 RegionView* rv = rs.front ();
5977 framepos_t pos = get_preferred_edit_position();
5981 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5982 /* edit point is outside the relevant region */
5987 if (pos <= rv->region()->position()) {
5991 len = pos - rv->region()->position();
5992 cmd = _("set fade in length");
5994 if (pos >= rv->region()->last_frame()) {
5998 len = rv->region()->last_frame() - pos;
5999 cmd = _("set fade out length");
6002 bool in_command = false;
6004 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6005 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6011 boost::shared_ptr<AutomationList> alist;
6013 alist = tmp->audio_region()->fade_in();
6015 alist = tmp->audio_region()->fade_out();
6018 XMLNode &before = alist->get_state();
6021 tmp->audio_region()->set_fade_in_length (len);
6022 tmp->audio_region()->set_fade_in_active (true);
6024 tmp->audio_region()->set_fade_out_length (len);
6025 tmp->audio_region()->set_fade_out_active (true);
6029 begin_reversible_command (cmd);
6032 XMLNode &after = alist->get_state();
6033 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6037 commit_reversible_command ();
6042 Editor::set_fade_in_shape (FadeShape shape)
6044 RegionSelection rs = get_regions_from_selection_and_entered ();
6049 bool in_command = false;
6051 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6052 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6058 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6059 XMLNode &before = alist->get_state();
6061 tmp->audio_region()->set_fade_in_shape (shape);
6064 begin_reversible_command (_("set fade in shape"));
6067 XMLNode &after = alist->get_state();
6068 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6072 commit_reversible_command ();
6077 Editor::set_fade_out_shape (FadeShape shape)
6079 RegionSelection rs = get_regions_from_selection_and_entered ();
6084 bool in_command = false;
6086 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6087 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6093 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6094 XMLNode &before = alist->get_state();
6096 tmp->audio_region()->set_fade_out_shape (shape);
6099 begin_reversible_command (_("set fade out shape"));
6102 XMLNode &after = alist->get_state();
6103 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6107 commit_reversible_command ();
6112 Editor::set_fade_in_active (bool yn)
6114 RegionSelection rs = get_regions_from_selection_and_entered ();
6119 bool in_command = false;
6121 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6122 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6129 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6131 ar->clear_changes ();
6132 ar->set_fade_in_active (yn);
6135 begin_reversible_command (_("set fade in active"));
6138 _session->add_command (new StatefulDiffCommand (ar));
6142 commit_reversible_command ();
6147 Editor::set_fade_out_active (bool yn)
6149 RegionSelection rs = get_regions_from_selection_and_entered ();
6154 bool in_command = false;
6156 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6157 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6163 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6165 ar->clear_changes ();
6166 ar->set_fade_out_active (yn);
6169 begin_reversible_command (_("set fade out active"));
6172 _session->add_command(new StatefulDiffCommand (ar));
6176 commit_reversible_command ();
6181 Editor::toggle_region_fades (int dir)
6183 if (_ignore_region_action) {
6187 boost::shared_ptr<AudioRegion> ar;
6190 RegionSelection rs = get_regions_from_selection_and_entered ();
6196 RegionSelection::iterator i;
6197 for (i = rs.begin(); i != rs.end(); ++i) {
6198 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6200 yn = ar->fade_out_active ();
6202 yn = ar->fade_in_active ();
6208 if (i == rs.end()) {
6212 /* XXX should this undo-able? */
6213 bool in_command = false;
6215 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6216 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6219 ar->clear_changes ();
6221 if (dir == 1 || dir == 0) {
6222 ar->set_fade_in_active (!yn);
6225 if (dir == -1 || dir == 0) {
6226 ar->set_fade_out_active (!yn);
6229 begin_reversible_command (_("toggle fade active"));
6232 _session->add_command(new StatefulDiffCommand (ar));
6236 commit_reversible_command ();
6241 /** Update region fade visibility after its configuration has been changed */
6243 Editor::update_region_fade_visibility ()
6245 bool _fade_visibility = _session->config.get_show_region_fades ();
6247 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6248 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6250 if (_fade_visibility) {
6251 v->audio_view()->show_all_fades ();
6253 v->audio_view()->hide_all_fades ();
6260 Editor::set_edit_point ()
6263 MusicFrame where (0, 0);
6265 if (!mouse_frame (where.frame, ignored)) {
6271 if (selection->markers.empty()) {
6273 mouse_add_new_marker (where.frame);
6278 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6281 loc->move_to (where.frame, where.division);
6287 Editor::set_playhead_cursor ()
6289 if (entered_marker) {
6290 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6292 MusicFrame where (0, 0);
6295 if (!mouse_frame (where.frame, ignored)) {
6302 _session->request_locate (where.frame, _session->transport_rolling());
6306 //not sure what this was for; remove it for now.
6307 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6308 // cancel_time_selection();
6314 Editor::split_region ()
6316 if (_drags->active ()) {
6320 //if a range is selected, separate it
6321 if ( !selection->time.empty()) {
6322 separate_regions_between (selection->time);
6326 //if no range was selected, try to find some regions to split
6327 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6329 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6330 const framepos_t pos = get_preferred_edit_position();
6331 const int32_t division = get_grid_music_divisions (0);
6332 MusicFrame where (pos, division);
6338 split_regions_at (where, rs);
6344 Editor::select_next_stripable (bool routes_only)
6346 if (selection->tracks.empty()) {
6347 selection->set (track_views.front());
6351 TimeAxisView* current = selection->tracks.front();
6355 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6357 if (*i == current) {
6359 if (i != track_views.end()) {
6362 current = (*(track_views.begin()));
6363 //selection->set (*(track_views.begin()));
6370 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6371 valid = rui && rui->route()->active();
6373 valid = 0 != current->stripable ().get();
6376 } while (current->hidden() || !valid);
6378 selection->set (current);
6380 ensure_time_axis_view_is_visible (*current, false);
6384 Editor::select_prev_stripable (bool routes_only)
6386 if (selection->tracks.empty()) {
6387 selection->set (track_views.front());
6391 TimeAxisView* current = selection->tracks.front();
6395 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6397 if (*i == current) {
6399 if (i != track_views.rend()) {
6402 current = *(track_views.rbegin());
6408 RouteUI* rui = dynamic_cast<RouteUI *>(current);
6409 valid = rui && rui->route()->active();
6411 valid = 0 != current->stripable ().get();
6414 } while (current->hidden() || !valid);
6416 selection->set (current);
6418 ensure_time_axis_view_is_visible (*current, false);
6422 Editor::set_loop_from_selection (bool play)
6424 if (_session == 0) {
6428 framepos_t start, end;
6429 if (!get_selection_extents ( start, end))
6432 set_loop_range (start, end, _("set loop range from selection"));
6435 _session->request_play_loop (true, true);
6440 Editor::set_loop_from_region (bool play)
6442 framepos_t start, end;
6443 if (!get_selection_extents ( start, end))
6446 set_loop_range (start, end, _("set loop range from region"));
6449 _session->request_locate (start, true);
6450 _session->request_play_loop (true);
6455 Editor::set_punch_from_selection ()
6457 if (_session == 0) {
6461 framepos_t start, end;
6462 if (!get_selection_extents ( start, end))
6465 set_punch_range (start, end, _("set punch range from selection"));
6469 Editor::set_auto_punch_range ()
6471 // auto punch in/out button from a single button
6472 // If Punch In is unset, set punch range from playhead to end, enable punch in
6473 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6474 // rewound beyond the Punch In marker, in which case that marker will be moved back
6475 // to the current playhead position.
6476 // If punch out is set, it clears the punch range and Punch In/Out buttons
6478 if (_session == 0) {
6482 Location* tpl = transport_punch_location();
6483 framepos_t now = playhead_cursor->current_frame();
6484 framepos_t begin = now;
6485 framepos_t end = _session->current_end_frame();
6487 if (!_session->config.get_punch_in()) {
6488 // First Press - set punch in and create range from here to eternity
6489 set_punch_range (begin, end, _("Auto Punch In"));
6490 _session->config.set_punch_in(true);
6491 } else if (tpl && !_session->config.get_punch_out()) {
6492 // Second press - update end range marker and set punch_out
6493 if (now < tpl->start()) {
6494 // playhead has been rewound - move start back and pretend nothing happened
6496 set_punch_range (begin, end, _("Auto Punch In/Out"));
6498 // normal case for 2nd press - set the punch out
6499 end = playhead_cursor->current_frame ();
6500 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6501 _session->config.set_punch_out(true);
6504 if (_session->config.get_punch_out()) {
6505 _session->config.set_punch_out(false);
6508 if (_session->config.get_punch_in()) {
6509 _session->config.set_punch_in(false);
6514 // third press - unset punch in/out and remove range
6515 _session->locations()->remove(tpl);
6522 Editor::set_session_extents_from_selection ()
6524 if (_session == 0) {
6528 framepos_t start, end;
6529 if (!get_selection_extents ( start, end))
6533 if ((loc = _session->locations()->session_range_location()) == 0) {
6534 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6536 XMLNode &before = loc->get_state();
6538 _session->set_session_extents (start, end);
6540 XMLNode &after = loc->get_state();
6542 begin_reversible_command (_("set session start/end from selection"));
6544 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6546 commit_reversible_command ();
6549 _session->set_end_is_free (false);
6553 Editor::set_punch_start_from_edit_point ()
6557 MusicFrame start (0, 0);
6558 framepos_t end = max_framepos;
6560 //use the existing punch end, if any
6561 Location* tpl = transport_punch_location();
6566 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6567 start.frame = _session->audible_frame();
6569 start.frame = get_preferred_edit_position();
6572 //snap the selection start/end
6575 //if there's not already a sensible selection endpoint, go "forever"
6576 if (start.frame > end ) {
6580 set_punch_range (start.frame, end, _("set punch start from EP"));
6586 Editor::set_punch_end_from_edit_point ()
6590 framepos_t start = 0;
6591 MusicFrame end (max_framepos, 0);
6593 //use the existing punch start, if any
6594 Location* tpl = transport_punch_location();
6596 start = tpl->start();
6599 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6600 end.frame = _session->audible_frame();
6602 end.frame = get_preferred_edit_position();
6605 //snap the selection start/end
6608 set_punch_range (start, end.frame, _("set punch end from EP"));
6614 Editor::set_loop_start_from_edit_point ()
6618 MusicFrame start (0, 0);
6619 framepos_t end = max_framepos;
6621 //use the existing loop end, if any
6622 Location* tpl = transport_loop_location();
6627 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6628 start.frame = _session->audible_frame();
6630 start.frame = get_preferred_edit_position();
6633 //snap the selection start/end
6636 //if there's not already a sensible selection endpoint, go "forever"
6637 if (start.frame > end ) {
6641 set_loop_range (start.frame, end, _("set loop start from EP"));
6647 Editor::set_loop_end_from_edit_point ()
6651 framepos_t start = 0;
6652 MusicFrame end (max_framepos, 0);
6654 //use the existing loop start, if any
6655 Location* tpl = transport_loop_location();
6657 start = tpl->start();
6660 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6661 end.frame = _session->audible_frame();
6663 end.frame = get_preferred_edit_position();
6666 //snap the selection start/end
6669 set_loop_range (start, end.frame, _("set loop end from EP"));
6674 Editor::set_punch_from_region ()
6676 framepos_t start, end;
6677 if (!get_selection_extents ( start, end))
6680 set_punch_range (start, end, _("set punch range from region"));
6684 Editor::pitch_shift_region ()
6686 RegionSelection rs = get_regions_from_selection_and_entered ();
6688 RegionSelection audio_rs;
6689 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6690 if (dynamic_cast<AudioRegionView*> (*i)) {
6691 audio_rs.push_back (*i);
6695 if (audio_rs.empty()) {
6699 pitch_shift (audio_rs, 1.2);
6703 Editor::set_tempo_from_region ()
6705 RegionSelection rs = get_regions_from_selection_and_entered ();
6707 if (!_session || rs.empty()) {
6711 RegionView* rv = rs.front();
6713 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6717 Editor::use_range_as_bar ()
6719 framepos_t start, end;
6720 if (get_edit_op_range (start, end)) {
6721 define_one_bar (start, end);
6726 Editor::define_one_bar (framepos_t start, framepos_t end)
6728 framepos_t length = end - start;
6730 const Meter& m (_session->tempo_map().meter_at_frame (start));
6732 /* length = 1 bar */
6734 /* We're going to deliver a constant tempo here,
6735 so we can use frames per beat to determine length.
6736 now we want frames per beat.
6737 we have frames per bar, and beats per bar, so ...
6740 /* XXXX METER MATH */
6742 double frames_per_beat = length / m.divisions_per_bar();
6744 /* beats per minute = */
6746 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6748 /* now decide whether to:
6750 (a) set global tempo
6751 (b) add a new tempo marker
6755 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6757 bool do_global = false;
6759 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6761 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6762 at the start, or create a new marker
6765 vector<string> options;
6766 options.push_back (_("Cancel"));
6767 options.push_back (_("Add new marker"));
6768 options.push_back (_("Set global tempo"));
6771 _("Define one bar"),
6772 _("Do you want to set the global tempo or add a new tempo marker?"),
6776 c.set_default_response (2);
6792 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6793 if the marker is at the region starter, change it, otherwise add
6798 begin_reversible_command (_("set tempo from region"));
6799 XMLNode& before (_session->tempo_map().get_state());
6802 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6803 } else if (t.frame() == start) {
6804 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6806 /* constant tempo */
6807 const Tempo tempo (beats_per_minute, t.note_type());
6808 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6811 XMLNode& after (_session->tempo_map().get_state());
6813 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6814 commit_reversible_command ();
6818 Editor::split_region_at_transients ()
6820 AnalysisFeatureList positions;
6822 RegionSelection rs = get_regions_from_selection_and_entered ();
6824 if (!_session || rs.empty()) {
6828 begin_reversible_command (_("split regions"));
6830 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6832 RegionSelection::iterator tmp;
6837 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6840 ar->transients (positions);
6841 split_region_at_points ((*i)->region(), positions, true);
6848 commit_reversible_command ();
6853 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6855 bool use_rhythmic_rodent = false;
6857 boost::shared_ptr<Playlist> pl = r->playlist();
6859 list<boost::shared_ptr<Region> > new_regions;
6865 if (positions.empty()) {
6869 if (positions.size() > 20 && can_ferret) {
6870 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);
6871 MessageDialog msg (msgstr,
6874 Gtk::BUTTONS_OK_CANCEL);
6877 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6878 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6880 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6883 msg.set_title (_("Excessive split?"));
6886 int response = msg.run();
6892 case RESPONSE_APPLY:
6893 use_rhythmic_rodent = true;
6900 if (use_rhythmic_rodent) {
6901 show_rhythm_ferret ();
6905 AnalysisFeatureList::const_iterator x;
6907 pl->clear_changes ();
6908 pl->clear_owned_changes ();
6910 x = positions.begin();
6912 if (x == positions.end()) {
6917 pl->remove_region (r);
6921 framepos_t rstart = r->first_frame ();
6922 framepos_t rend = r->last_frame ();
6924 while (x != positions.end()) {
6926 /* deal with positons that are out of scope of present region bounds */
6927 if (*x <= rstart || *x > rend) {
6932 /* file start = original start + how far we from the initial position ? */
6934 framepos_t file_start = r->start() + pos;
6936 /* length = next position - current position */
6938 framepos_t len = (*x) - pos - rstart;
6940 /* XXX we do we really want to allow even single-sample regions?
6941 * shouldn't we have some kind of lower limit on region size?
6950 if (RegionFactory::region_name (new_name, r->name())) {
6954 /* do NOT announce new regions 1 by one, just wait till they are all done */
6958 plist.add (ARDOUR::Properties::start, file_start);
6959 plist.add (ARDOUR::Properties::length, len);
6960 plist.add (ARDOUR::Properties::name, new_name);
6961 plist.add (ARDOUR::Properties::layer, 0);
6962 // TODO set transients_offset
6964 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6965 /* because we set annouce to false, manually add the new region to the
6968 RegionFactory::map_add (nr);
6970 pl->add_region (nr, rstart + pos);
6973 new_regions.push_front(nr);
6982 RegionFactory::region_name (new_name, r->name());
6984 /* Add the final region */
6987 plist.add (ARDOUR::Properties::start, r->start() + pos);
6988 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6989 plist.add (ARDOUR::Properties::name, new_name);
6990 plist.add (ARDOUR::Properties::layer, 0);
6992 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6993 /* because we set annouce to false, manually add the new region to the
6996 RegionFactory::map_add (nr);
6997 pl->add_region (nr, r->position() + pos);
7000 new_regions.push_front(nr);
7005 /* We might have removed regions, which alters other regions' layering_index,
7006 so we need to do a recursive diff here.
7008 vector<Command*> cmds;
7010 _session->add_commands (cmds);
7012 _session->add_command (new StatefulDiffCommand (pl));
7016 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
7017 set_selected_regionview_from_region_list ((*i), Selection::Add);
7023 Editor::place_transient()
7029 RegionSelection rs = get_regions_from_selection_and_edit_point ();
7035 framepos_t where = get_preferred_edit_position();
7037 begin_reversible_command (_("place transient"));
7039 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7040 (*r)->region()->add_transient(where);
7043 commit_reversible_command ();
7047 Editor::remove_transient(ArdourCanvas::Item* item)
7053 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7056 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7057 _arv->remove_transient (*(float*) _line->get_data ("position"));
7061 Editor::snap_regions_to_grid ()
7063 list <boost::shared_ptr<Playlist > > used_playlists;
7065 RegionSelection rs = get_regions_from_selection_and_entered ();
7067 if (!_session || rs.empty()) {
7071 begin_reversible_command (_("snap regions to grid"));
7073 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7075 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7077 if (!pl->frozen()) {
7078 /* we haven't seen this playlist before */
7080 /* remember used playlists so we can thaw them later */
7081 used_playlists.push_back(pl);
7084 (*r)->region()->clear_changes ();
7086 MusicFrame start ((*r)->region()->first_frame (), 0);
7088 (*r)->region()->set_position (start.frame, start.division);
7089 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7092 while (used_playlists.size() > 0) {
7093 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7095 used_playlists.pop_front();
7098 commit_reversible_command ();
7102 Editor::close_region_gaps ()
7104 list <boost::shared_ptr<Playlist > > used_playlists;
7106 RegionSelection rs = get_regions_from_selection_and_entered ();
7108 if (!_session || rs.empty()) {
7112 Dialog dialog (_("Close Region Gaps"));
7115 table.set_spacings (12);
7116 table.set_border_width (12);
7117 Label* l = manage (left_aligned_label (_("Crossfade length")));
7118 table.attach (*l, 0, 1, 0, 1);
7120 SpinButton spin_crossfade (1, 0);
7121 spin_crossfade.set_range (0, 15);
7122 spin_crossfade.set_increments (1, 1);
7123 spin_crossfade.set_value (5);
7124 table.attach (spin_crossfade, 1, 2, 0, 1);
7126 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7128 l = manage (left_aligned_label (_("Pull-back length")));
7129 table.attach (*l, 0, 1, 1, 2);
7131 SpinButton spin_pullback (1, 0);
7132 spin_pullback.set_range (0, 100);
7133 spin_pullback.set_increments (1, 1);
7134 spin_pullback.set_value(30);
7135 table.attach (spin_pullback, 1, 2, 1, 2);
7137 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7139 dialog.get_vbox()->pack_start (table);
7140 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7141 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7144 if (dialog.run () == RESPONSE_CANCEL) {
7148 framepos_t crossfade_len = spin_crossfade.get_value();
7149 framepos_t pull_back_frames = spin_pullback.get_value();
7151 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7152 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7154 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7156 begin_reversible_command (_("close region gaps"));
7159 boost::shared_ptr<Region> last_region;
7161 rs.sort_by_position_and_track();
7163 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7165 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7167 if (!pl->frozen()) {
7168 /* we haven't seen this playlist before */
7170 /* remember used playlists so we can thaw them later */
7171 used_playlists.push_back(pl);
7175 framepos_t position = (*r)->region()->position();
7177 if (idx == 0 || position < last_region->position()){
7178 last_region = (*r)->region();
7183 (*r)->region()->clear_changes ();
7184 (*r)->region()->trim_front( (position - pull_back_frames));
7186 last_region->clear_changes ();
7187 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7189 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7190 _session->add_command (new StatefulDiffCommand (last_region));
7192 last_region = (*r)->region();
7196 while (used_playlists.size() > 0) {
7197 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7199 used_playlists.pop_front();
7202 commit_reversible_command ();
7206 Editor::tab_to_transient (bool forward)
7208 AnalysisFeatureList positions;
7210 RegionSelection rs = get_regions_from_selection_and_entered ();
7216 framepos_t pos = _session->audible_frame ();
7218 if (!selection->tracks.empty()) {
7220 /* don't waste time searching for transients in duplicate playlists.
7223 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7225 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7227 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7230 boost::shared_ptr<Track> tr = rtv->track();
7232 boost::shared_ptr<Playlist> pl = tr->playlist ();
7234 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7237 positions.push_back (result);
7250 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7251 (*r)->region()->get_transients (positions);
7255 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7258 AnalysisFeatureList::iterator x;
7260 for (x = positions.begin(); x != positions.end(); ++x) {
7266 if (x != positions.end ()) {
7267 _session->request_locate (*x);
7271 AnalysisFeatureList::reverse_iterator x;
7273 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7279 if (x != positions.rend ()) {
7280 _session->request_locate (*x);
7286 Editor::playhead_forward_to_grid ()
7292 MusicFrame pos (playhead_cursor->current_frame (), 0);
7294 if (pos.frame < max_framepos - 1) {
7296 snap_to_internal (pos, RoundUpAlways, false, true);
7297 _session->request_locate (pos.frame);
7303 Editor::playhead_backward_to_grid ()
7309 MusicFrame pos (playhead_cursor->current_frame (), 0);
7311 if (pos.frame > 2) {
7313 snap_to_internal (pos, RoundDownAlways, false, true);
7314 _session->request_locate (pos.frame);
7319 Editor::set_track_height (Height h)
7321 TrackSelection& ts (selection->tracks);
7323 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7324 (*x)->set_height_enum (h);
7329 Editor::toggle_tracks_active ()
7331 TrackSelection& ts (selection->tracks);
7333 bool target = false;
7339 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7340 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7344 target = !rtv->_route->active();
7347 rtv->_route->set_active (target, this);
7353 Editor::remove_tracks ()
7355 /* this will delete GUI objects that may be the subject of an event
7356 handler in which this method is called. Defer actual deletion to the
7357 next idle callback, when all event handling is finished.
7359 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7363 Editor::idle_remove_tracks ()
7365 Session::StateProtector sp (_session);
7367 return false; /* do not call again */
7371 Editor::_remove_tracks ()
7373 TrackSelection& ts (selection->tracks);
7379 vector<string> choices;
7384 const char* trackstr;
7387 vector<boost::shared_ptr<Route> > routes;
7388 vector<boost::shared_ptr<VCA> > vcas;
7389 bool special_bus = false;
7391 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7392 VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
7394 vcas.push_back (vtv->vca());
7398 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7402 if (rtv->is_track()) {
7407 routes.push_back (rtv->_route);
7409 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7414 if (special_bus && !Config->get_allow_special_bus_removal()) {
7415 MessageDialog msg (_("That would be bad news ...."),
7419 msg.set_secondary_text (string_compose (_(
7420 "Removing the master or monitor bus is such a bad idea\n\
7421 that %1 is not going to allow it.\n\
7423 If you really want to do this sort of thing\n\
7424 edit your ardour.rc file to set the\n\
7425 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7432 if (ntracks + nbusses + nvcas == 0) {
7438 trackstr = P_("track", "tracks", ntracks);
7439 busstr = P_("bus", "busses", nbusses);
7440 vcastr = P_("VCA", "VCAs", nvcas);
7442 if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
7443 title = _("Remove various strips");
7444 prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
7445 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
7447 else if (ntracks > 0 && nbusses > 0) {
7448 title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
7449 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7450 ntracks, trackstr, nbusses, busstr);
7452 else if (ntracks > 0 && nvcas > 0) {
7453 title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
7454 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7455 ntracks, trackstr, nvcas, vcastr);
7457 else if (nbusses > 0 && nvcas > 0) {
7458 title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
7459 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
7460 nbusses, busstr, nvcas, vcastr);
7462 else if (ntracks > 0) {
7463 title = string_compose (_("Remove %1"), trackstr);
7464 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7467 else if (nbusses > 0) {
7468 title = string_compose (_("Remove %1"), busstr);
7469 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7472 else if (nvcas > 0) {
7473 title = string_compose (_("Remove %1"), vcastr);
7474 prompt = string_compose (_("Do you really want to remove %1 %2?"),
7482 prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
7485 prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
7487 choices.push_back (_("No, do nothing."));
7488 if (ntracks + nbusses + nvcas > 1) {
7489 choices.push_back (_("Yes, remove them."));
7491 choices.push_back (_("Yes, remove it."));
7494 Choice prompter (title, prompt, choices);
7496 if (prompter.run () != 1) {
7500 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7501 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7502 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7503 * likely because deletion requires selection) this will call
7504 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7505 * It's likewise likely that the route that has just been displayed in the
7506 * Editor-Mixer will be next in line for deletion.
7508 * So simply switch to the master-bus (if present)
7510 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7511 if ((*i)->stripable ()->is_master ()) {
7512 set_selected_mixer_strip (*(*i));
7519 PresentationInfo::ChangeSuspender cs;
7520 DisplaySuspender ds;
7522 boost::shared_ptr<RouteList> rl (new RouteList);
7523 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7526 _session->remove_routes (rl);
7528 for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
7529 _session->vca_manager().remove_vca (*x);
7533 /* TrackSelection and RouteList leave scope,
7534 * destructors are called,
7535 * diskstream drops references, save_state is called (again for every track)
7540 Editor::do_insert_time ()
7542 if (selection->tracks.empty()) {
7546 InsertRemoveTimeDialog d (*this);
7547 int response = d.run ();
7549 if (response != RESPONSE_OK) {
7553 if (d.distance() == 0) {
7560 d.intersected_region_action (),
7564 d.move_glued_markers(),
7565 d.move_locked_markers(),
7571 Editor::insert_time (
7572 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7573 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7577 if (Config->get_edit_mode() == Lock) {
7580 bool in_command = false;
7582 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7584 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7588 /* don't operate on any playlist more than once, which could
7589 * happen if "all playlists" is enabled, but there is more
7590 * than 1 track using playlists "from" a given track.
7593 set<boost::shared_ptr<Playlist> > pl;
7595 if (all_playlists) {
7596 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7597 if (rtav && rtav->track ()) {
7598 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7599 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7604 if ((*x)->playlist ()) {
7605 pl.insert ((*x)->playlist ());
7609 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7611 (*i)->clear_changes ();
7612 (*i)->clear_owned_changes ();
7615 begin_reversible_command (_("insert time"));
7619 if (opt == SplitIntersected) {
7620 /* non musical split */
7621 (*i)->split (MusicFrame (pos, 0));
7624 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7626 vector<Command*> cmds;
7628 _session->add_commands (cmds);
7630 _session->add_command (new StatefulDiffCommand (*i));
7634 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7637 begin_reversible_command (_("insert time"));
7640 rtav->route ()->shift (pos, frames);
7647 const int32_t divisions = get_grid_music_divisions (0);
7648 XMLNode& before (_session->locations()->get_state());
7649 Locations::LocationList copy (_session->locations()->list());
7651 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7653 Locations::LocationList::const_iterator tmp;
7655 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7656 bool const was_locked = (*i)->locked ();
7657 if (locked_markers_too) {
7661 if ((*i)->start() >= pos) {
7662 // move end first, in case we're moving by more than the length of the range
7663 if (!(*i)->is_mark()) {
7664 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7666 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7678 begin_reversible_command (_("insert time"));
7681 XMLNode& after (_session->locations()->get_state());
7682 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7688 begin_reversible_command (_("insert time"));
7691 XMLNode& before (_session->tempo_map().get_state());
7692 _session->tempo_map().insert_time (pos, frames);
7693 XMLNode& after (_session->tempo_map().get_state());
7694 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7698 commit_reversible_command ();
7703 Editor::do_remove_time ()
7705 if (selection->tracks.empty()) {
7709 InsertRemoveTimeDialog d (*this, true);
7711 int response = d.run ();
7713 if (response != RESPONSE_OK) {
7717 framecnt_t distance = d.distance();
7719 if (distance == 0) {
7729 d.move_glued_markers(),
7730 d.move_locked_markers(),
7736 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7737 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7739 if (Config->get_edit_mode() == Lock) {
7740 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7743 bool in_command = false;
7745 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7747 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7751 XMLNode &before = pl->get_state();
7754 begin_reversible_command (_("remove time"));
7758 std::list<AudioRange> rl;
7759 AudioRange ar(pos, pos+frames, 0);
7762 pl->shift (pos, -frames, true, ignore_music_glue);
7764 XMLNode &after = pl->get_state();
7766 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7770 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7773 begin_reversible_command (_("remove time"));
7776 rtav->route ()->shift (pos, -frames);
7780 const int32_t divisions = get_grid_music_divisions (0);
7781 std::list<Location*> loc_kill_list;
7786 XMLNode& before (_session->locations()->get_state());
7787 Locations::LocationList copy (_session->locations()->list());
7789 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7790 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7792 bool const was_locked = (*i)->locked ();
7793 if (locked_markers_too) {
7797 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7798 if ((*i)->end() >= pos
7799 && (*i)->end() < pos+frames
7800 && (*i)->start() >= pos
7801 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7803 loc_kill_list.push_back(*i);
7804 } else { // only start or end is included, try to do the right thing
7805 // move start before moving end, to avoid trying to move the end to before the start
7806 // if we're removing more time than the length of the range
7807 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7808 // start is within cut
7809 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7811 } else if ((*i)->start() >= pos+frames) {
7812 // start (and thus entire range) lies beyond end of cut
7813 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7816 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7817 // end is inside cut
7818 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7820 } else if ((*i)->end() >= pos+frames) {
7821 // end is beyond end of cut
7822 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7827 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7828 loc_kill_list.push_back(*i);
7830 } else if ((*i)->start() >= pos) {
7831 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7841 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7842 _session->locations()->remove( *i );
7847 begin_reversible_command (_("remove time"));
7850 XMLNode& after (_session->locations()->get_state());
7851 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7856 XMLNode& before (_session->tempo_map().get_state());
7858 if (_session->tempo_map().remove_time (pos, frames) ) {
7860 begin_reversible_command (_("remove time"));
7863 XMLNode& after (_session->tempo_map().get_state());
7864 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7869 commit_reversible_command ();
7874 Editor::fit_selection ()
7876 if (!selection->tracks.empty()) {
7877 fit_tracks (selection->tracks);
7881 /* no selected tracks - use tracks with selected regions */
7883 if (!selection->regions.empty()) {
7884 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7885 tvl.push_back (&(*r)->get_time_axis_view ());
7891 } else if (internal_editing()) {
7892 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7895 if (entered_track) {
7896 tvl.push_back (entered_track);
7904 Editor::fit_tracks (TrackViewList & tracks)
7906 if (tracks.empty()) {
7910 uint32_t child_heights = 0;
7911 int visible_tracks = 0;
7913 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7915 if (!(*t)->marked_for_display()) {
7919 child_heights += (*t)->effective_height() - (*t)->current_height();
7923 /* compute the per-track height from:
7925 * total canvas visible height
7926 * - height that will be taken by visible children of selected tracks
7927 * - height of the ruler/hscroll area
7929 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7930 double first_y_pos = DBL_MAX;
7932 if (h < TimeAxisView::preset_height (HeightSmall)) {
7933 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7934 /* too small to be displayed */
7938 undo_visual_stack.push_back (current_visual_state (true));
7939 PBD::Unwinder<bool> nsv (no_save_visual, true);
7941 /* build a list of all tracks, including children */
7944 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7946 TimeAxisView::Children c = (*i)->get_child_list ();
7947 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7948 all.push_back (j->get());
7953 // find selection range.
7954 // if someone knows how to user TrackViewList::iterator for this
7956 int selected_top = -1;
7957 int selected_bottom = -1;
7959 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7960 if ((*t)->marked_for_display ()) {
7961 if (tracks.contains(*t)) {
7962 if (selected_top == -1) {
7965 selected_bottom = i;
7971 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7972 if ((*t)->marked_for_display ()) {
7973 if (tracks.contains(*t)) {
7974 (*t)->set_height (h);
7975 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7977 if (i > selected_top && i < selected_bottom) {
7978 hide_track_in_display (*t);
7985 set the controls_layout height now, because waiting for its size
7986 request signal handler will cause the vertical adjustment setting to fail
7989 controls_layout.property_height () = _full_canvas_height;
7990 vertical_adjustment.set_value (first_y_pos);
7992 redo_visual_stack.push_back (current_visual_state (true));
7994 visible_tracks_selector.set_text (_("Sel"));
7998 Editor::save_visual_state (uint32_t n)
8000 while (visual_states.size() <= n) {
8001 visual_states.push_back (0);
8004 if (visual_states[n] != 0) {
8005 delete visual_states[n];
8008 visual_states[n] = current_visual_state (true);
8013 Editor::goto_visual_state (uint32_t n)
8015 if (visual_states.size() <= n) {
8019 if (visual_states[n] == 0) {
8023 use_visual_state (*visual_states[n]);
8027 Editor::start_visual_state_op (uint32_t n)
8029 save_visual_state (n);
8031 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
8033 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
8034 pup->set_text (buf);
8039 Editor::cancel_visual_state_op (uint32_t n)
8041 goto_visual_state (n);
8045 Editor::toggle_region_mute ()
8047 if (_ignore_region_action) {
8051 RegionSelection rs = get_regions_from_selection_and_entered ();
8057 if (rs.size() > 1) {
8058 begin_reversible_command (_("mute regions"));
8060 begin_reversible_command (_("mute region"));
8063 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
8065 (*i)->region()->playlist()->clear_changes ();
8066 (*i)->region()->set_muted (!(*i)->region()->muted ());
8067 _session->add_command (new StatefulDiffCommand ((*i)->region()));
8071 commit_reversible_command ();
8075 Editor::combine_regions ()
8077 /* foreach track with selected regions, take all selected regions
8078 and join them into a new region containing the subregions (as a
8082 typedef set<RouteTimeAxisView*> RTVS;
8085 if (selection->regions.empty()) {
8089 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8090 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8093 tracks.insert (rtv);
8097 begin_reversible_command (_("combine regions"));
8099 vector<RegionView*> new_selection;
8101 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8104 if ((rv = (*i)->combine_regions ()) != 0) {
8105 new_selection.push_back (rv);
8109 selection->clear_regions ();
8110 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8111 selection->add (*i);
8114 commit_reversible_command ();
8118 Editor::uncombine_regions ()
8120 typedef set<RouteTimeAxisView*> RTVS;
8123 if (selection->regions.empty()) {
8127 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8128 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8131 tracks.insert (rtv);
8135 begin_reversible_command (_("uncombine regions"));
8137 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8138 (*i)->uncombine_regions ();
8141 commit_reversible_command ();
8145 Editor::toggle_midi_input_active (bool flip_others)
8148 boost::shared_ptr<RouteList> rl (new RouteList);
8150 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8151 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8157 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8160 rl->push_back (rtav->route());
8161 onoff = !mt->input_active();
8165 _session->set_exclusive_input_active (rl, onoff, flip_others);
8168 static bool ok_fine (GdkEventAny*) { return true; }
8174 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8176 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8177 lock_dialog->get_vbox()->pack_start (*padlock);
8178 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8180 ArdourButton* b = manage (new ArdourButton);
8181 b->set_name ("lock button");
8182 b->set_text (_("Click to unlock"));
8183 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8184 lock_dialog->get_vbox()->pack_start (*b);
8186 lock_dialog->get_vbox()->show_all ();
8187 lock_dialog->set_size_request (200, 200);
8190 delete _main_menu_disabler;
8191 _main_menu_disabler = new MainMenuDisabler;
8193 lock_dialog->present ();
8195 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8201 lock_dialog->hide ();
8203 delete _main_menu_disabler;
8204 _main_menu_disabler = 0;
8206 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8207 start_lock_event_timing ();
8212 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8214 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8218 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8220 Timers::TimerSuspender t;
8221 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8222 Gtkmm2ext::UI::instance()->flush_pending (1);
8226 Editor::bring_all_sources_into_session ()
8233 ArdourDialog w (_("Moving embedded files into session folder"));
8234 w.get_vbox()->pack_start (msg);
8237 /* flush all pending GUI events because we're about to start copying
8241 Timers::TimerSuspender t;
8242 Gtkmm2ext::UI::instance()->flush_pending (3);
8246 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));