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 "pbd/error.h"
32 #include "pbd/basename.h"
33 #include "pbd/pthread_utils.h"
34 #include "pbd/memento_command.h"
35 #include "pbd/unwind.h"
36 #include "pbd/whitespace.h"
37 #include "pbd/stateful_diff_command.h"
39 #include <gtkmm2ext/utils.h>
40 #include <gtkmm2ext/choice.h>
41 #include <gtkmm2ext/popup.h>
43 #include "ardour/audio_track.h"
44 #include "ardour/audioregion.h"
45 #include "ardour/boost_debug.h"
46 #include "ardour/dB.h"
47 #include "ardour/location.h"
48 #include "ardour/midi_region.h"
49 #include "ardour/midi_track.h"
50 #include "ardour/operations.h"
51 #include "ardour/playlist_factory.h"
52 #include "ardour/profile.h"
53 #include "ardour/quantize.h"
54 #include "ardour/legatize.h"
55 #include "ardour/region_factory.h"
56 #include "ardour/reverse.h"
57 #include "ardour/session.h"
58 #include "ardour/session_playlists.h"
59 #include "ardour/strip_silence.h"
60 #include "ardour/transient_detector.h"
61 #include "ardour/transpose.h"
63 #include "canvas/canvas.h"
66 #include "audio_region_view.h"
67 #include "audio_streamview.h"
68 #include "audio_time_axis.h"
69 #include "automation_region_view.h"
70 #include "automation_time_axis.h"
71 #include "control_point.h"
75 #include "editor_cursors.h"
76 #include "editor_drag.h"
77 #include "editor_regions.h"
78 #include "editor_routes.h"
79 #include "gui_thread.h"
80 #include "insert_remove_time_dialog.h"
81 #include "interthread_progress_window.h"
82 #include "item_counts.h"
84 #include "midi_region_view.h"
86 #include "mixer_strip.h"
87 #include "mouse_cursors.h"
88 #include "normalize_dialog.h"
90 #include "paste_context.h"
91 #include "patch_change_dialog.h"
92 #include "quantize_dialog.h"
93 #include "region_gain_line.h"
94 #include "rgb_macros.h"
95 #include "route_time_axis.h"
96 #include "selection.h"
97 #include "selection_templates.h"
98 #include "streamview.h"
99 #include "strip_silence_dialog.h"
100 #include "time_axis_view.h"
102 #include "transpose_dialog.h"
103 #include "transform_dialog.h"
104 #include "ui_config.h"
106 #include "pbd/i18n.h"
109 using namespace ARDOUR;
112 using namespace Gtkmm2ext;
113 using namespace ArdourWidgets;
114 using namespace Editing;
115 using Gtkmm2ext::Keyboard;
117 /***********************************************************************
119 ***********************************************************************/
122 Editor::undo (uint32_t n)
124 if (_session && _session->actively_recording()) {
125 /* no undo allowed while recording. Session will check also,
126 but we don't even want to get to that.
131 if (_drags->active ()) {
137 if (_session->undo_depth() == 0) {
138 undo_action->set_sensitive(false);
140 redo_action->set_sensitive(true);
141 begin_selection_op_history ();
146 Editor::redo (uint32_t n)
148 if (_session && _session->actively_recording()) {
149 /* no redo allowed while recording. Session will check also,
150 but we don't even want to get to that.
155 if (_drags->active ()) {
161 if (_session->redo_depth() == 0) {
162 redo_action->set_sensitive(false);
164 undo_action->set_sensitive(true);
165 begin_selection_op_history ();
170 Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
174 RegionSelection pre_selected_regions = selection->regions;
175 bool working_on_selection = !pre_selected_regions.empty();
177 list<boost::shared_ptr<Playlist> > used_playlists;
178 list<RouteTimeAxisView*> used_trackviews;
180 if (regions.empty()) {
184 begin_reversible_command (_("split"));
186 // if splitting a single region, and snap-to is using
187 // region boundaries, don't pay attention to them
189 if (regions.size() == 1) {
190 switch (_snap_type) {
191 case SnapToRegionStart:
192 case SnapToRegionSync:
193 case SnapToRegionEnd:
206 EditorFreeze(); /* Emit Signal */
209 for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) {
211 RegionSelection::iterator tmp;
213 /* XXX this test needs to be more complicated, to make sure we really
214 have something to split.
217 if (!(*a)->region()->covers (where.frame)) {
225 boost::shared_ptr<Playlist> pl = (*a)->region()->playlist();
233 /* we haven't seen this playlist before */
235 /* remember used playlists so we can thaw them later */
236 used_playlists.push_back(pl);
238 TimeAxisView& tv = (*a)->get_time_axis_view();
239 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
241 used_trackviews.push_back (rtv);
248 pl->clear_changes ();
249 pl->split_region ((*a)->region(), where);
250 _session->add_command (new StatefulDiffCommand (pl));
256 latest_regionviews.clear ();
258 vector<sigc::connection> region_added_connections;
260 for (list<RouteTimeAxisView*>::iterator i = used_trackviews.begin(); i != used_trackviews.end(); ++i) {
261 region_added_connections.push_back ((*i)->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view)));
264 while (used_playlists.size() > 0) {
265 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
267 used_playlists.pop_front();
270 for (vector<sigc::connection>::iterator c = region_added_connections.begin(); c != region_added_connections.end(); ++c) {
275 EditorThaw(); /* Emit Signal */
278 if (working_on_selection) {
279 // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
281 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
282 /* There are three classes of regions that we might want selected after
283 splitting selected regions:
284 - regions selected before the split operation, and unaffected by it
285 - newly-created regions before the split
286 - newly-created regions after the split
289 if (rsas & Existing) {
290 // region selections that existed before the split.
291 selection->add ( pre_selected_regions );
294 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
295 if ((*ri)->region()->position() < where.frame) {
296 // new regions created before the split
297 if (rsas & NewlyCreatedLeft) {
298 selection->add (*ri);
301 // new regions created after the split
302 if (rsas & NewlyCreatedRight) {
303 selection->add (*ri);
308 if( working_on_selection ) {
309 selection->add (latest_regionviews); //these are the new regions created after the split
313 commit_reversible_command ();
316 /** Move one extreme of the current range selection. If more than one range is selected,
317 * the start of the earliest range or the end of the latest range is moved.
319 * @param move_end true to move the end of the current range selection, false to move
321 * @param next true to move the extreme to the next region boundary, false to move to
325 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
327 if (selection->time.start() == selection->time.end_frame()) {
331 framepos_t start = selection->time.start ();
332 framepos_t end = selection->time.end_frame ();
334 /* the position of the thing we may move */
335 framepos_t pos = move_end ? end : start;
336 int dir = next ? 1 : -1;
338 /* so we don't find the current region again */
339 if (dir > 0 || pos > 0) {
343 framepos_t const target = get_region_boundary (pos, dir, true, false);
358 begin_reversible_selection_op (_("alter selection"));
359 selection->set_preserving_all_ranges (start, end);
360 commit_reversible_selection_op ();
364 Editor::nudge_forward_release (GdkEventButton* ev)
366 if (ev->state & Keyboard::PrimaryModifier) {
367 nudge_forward (false, true);
369 nudge_forward (false, false);
375 Editor::nudge_backward_release (GdkEventButton* ev)
377 if (ev->state & Keyboard::PrimaryModifier) {
378 nudge_backward (false, true);
380 nudge_backward (false, false);
387 Editor::nudge_forward (bool next, bool force_playhead)
390 framepos_t next_distance;
396 RegionSelection rs = get_regions_from_selection_and_entered ();
398 if (!force_playhead && !rs.empty()) {
400 begin_reversible_command (_("nudge regions forward"));
402 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
403 boost::shared_ptr<Region> r ((*i)->region());
405 distance = get_nudge_distance (r->position(), next_distance);
408 distance = next_distance;
412 r->set_position (r->position() + distance);
413 _session->add_command (new StatefulDiffCommand (r));
416 commit_reversible_command ();
419 } else if (!force_playhead && !selection->markers.empty()) {
422 bool in_command = false;
423 const int32_t divisions = get_grid_music_divisions (0);
425 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
427 Location* loc = find_location_from_marker ((*i), is_start);
431 XMLNode& before (loc->get_state());
434 distance = get_nudge_distance (loc->start(), next_distance);
436 distance = next_distance;
438 if (max_framepos - distance > loc->start() + loc->length()) {
439 loc->set_start (loc->start() + distance, false, true, divisions);
441 loc->set_start (max_framepos - loc->length(), false, true, divisions);
444 distance = get_nudge_distance (loc->end(), next_distance);
446 distance = next_distance;
448 if (max_framepos - distance > loc->end()) {
449 loc->set_end (loc->end() + distance, false, true, divisions);
451 loc->set_end (max_framepos, false, true, divisions);
453 if (loc->is_session_range()) {
454 _session->set_end_is_free (false);
458 begin_reversible_command (_("nudge location forward"));
461 XMLNode& after (loc->get_state());
462 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
467 commit_reversible_command ();
470 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
471 _session->request_locate (playhead_cursor->current_frame () + distance);
476 Editor::nudge_backward (bool next, bool force_playhead)
479 framepos_t next_distance;
485 RegionSelection rs = get_regions_from_selection_and_entered ();
487 if (!force_playhead && !rs.empty()) {
489 begin_reversible_command (_("nudge regions backward"));
491 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
492 boost::shared_ptr<Region> r ((*i)->region());
494 distance = get_nudge_distance (r->position(), next_distance);
497 distance = next_distance;
502 if (r->position() > distance) {
503 r->set_position (r->position() - distance);
507 _session->add_command (new StatefulDiffCommand (r));
510 commit_reversible_command ();
512 } else if (!force_playhead && !selection->markers.empty()) {
515 bool in_command = false;
517 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
519 Location* loc = find_location_from_marker ((*i), is_start);
523 XMLNode& before (loc->get_state());
526 distance = get_nudge_distance (loc->start(), next_distance);
528 distance = next_distance;
530 if (distance < loc->start()) {
531 loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
533 loc->set_start (0, false, true, get_grid_music_divisions(0));
536 distance = get_nudge_distance (loc->end(), next_distance);
539 distance = next_distance;
542 if (distance < loc->end() - loc->length()) {
543 loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
545 loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
547 if (loc->is_session_range()) {
548 _session->set_end_is_free (false);
552 begin_reversible_command (_("nudge location forward"));
555 XMLNode& after (loc->get_state());
556 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
560 commit_reversible_command ();
565 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
567 if (playhead_cursor->current_frame () > distance) {
568 _session->request_locate (playhead_cursor->current_frame () - distance);
570 _session->goto_start();
576 Editor::nudge_forward_capture_offset ()
578 RegionSelection rs = get_regions_from_selection_and_entered ();
580 if (!_session || rs.empty()) {
584 begin_reversible_command (_("nudge forward"));
586 framepos_t const distance = _session->worst_output_latency();
588 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
589 boost::shared_ptr<Region> r ((*i)->region());
592 r->set_position (r->position() + distance);
593 _session->add_command(new StatefulDiffCommand (r));
596 commit_reversible_command ();
600 Editor::nudge_backward_capture_offset ()
602 RegionSelection rs = get_regions_from_selection_and_entered ();
604 if (!_session || rs.empty()) {
608 begin_reversible_command (_("nudge backward"));
610 framepos_t const distance = _session->worst_output_latency();
612 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
613 boost::shared_ptr<Region> r ((*i)->region());
617 if (r->position() > distance) {
618 r->set_position (r->position() - distance);
622 _session->add_command(new StatefulDiffCommand (r));
625 commit_reversible_command ();
628 struct RegionSelectionPositionSorter {
629 bool operator() (RegionView* a, RegionView* b) {
630 return a->region()->position() < b->region()->position();
635 Editor::sequence_regions ()
638 framepos_t r_end_prev;
646 RegionSelection rs = get_regions_from_selection_and_entered ();
647 rs.sort(RegionSelectionPositionSorter());
651 bool in_command = false;
653 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
654 boost::shared_ptr<Region> r ((*i)->region());
662 if(r->position_locked())
669 r->set_position(r_end_prev);
673 begin_reversible_command (_("sequence regions"));
676 _session->add_command (new StatefulDiffCommand (r));
678 r_end=r->position() + r->length();
684 commit_reversible_command ();
693 Editor::move_to_start ()
695 _session->goto_start ();
699 Editor::move_to_end ()
702 _session->request_locate (_session->current_end_frame());
706 Editor::build_region_boundary_cache ()
709 vector<RegionPoint> interesting_points;
710 boost::shared_ptr<Region> r;
711 TrackViewList tracks;
714 region_boundary_cache.clear ();
720 switch (_snap_type) {
721 case SnapToRegionStart:
722 interesting_points.push_back (Start);
724 case SnapToRegionEnd:
725 interesting_points.push_back (End);
727 case SnapToRegionSync:
728 interesting_points.push_back (SyncPoint);
730 case SnapToRegionBoundary:
731 interesting_points.push_back (Start);
732 interesting_points.push_back (End);
735 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
736 abort(); /*NOTREACHED*/
740 TimeAxisView *ontrack = 0;
743 if (!selection->tracks.empty()) {
744 tlist = selection->tracks.filter_to_unique_playlists ();
746 tlist = track_views.filter_to_unique_playlists ();
749 while (pos < _session->current_end_frame() && !at_end) {
752 framepos_t lpos = max_framepos;
754 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
756 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
757 if (*p == interesting_points.back()) {
760 /* move to next point type */
766 rpos = r->first_frame();
770 rpos = r->last_frame();
774 rpos = r->sync_position ();
782 RouteTimeAxisView *rtav;
784 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
785 if (rtav->track() != 0) {
786 speed = rtav->track()->speed();
790 rpos = track_frame_to_session_frame (rpos, speed);
796 /* prevent duplicates, but we don't use set<> because we want to be able
800 vector<framepos_t>::iterator ri;
802 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
808 if (ri == region_boundary_cache.end()) {
809 region_boundary_cache.push_back (rpos);
816 /* finally sort to be sure that the order is correct */
818 sort (region_boundary_cache.begin(), region_boundary_cache.end());
821 boost::shared_ptr<Region>
822 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
824 TrackViewList::iterator i;
825 framepos_t closest = max_framepos;
826 boost::shared_ptr<Region> ret;
830 framepos_t track_frame;
831 RouteTimeAxisView *rtav;
833 for (i = tracks.begin(); i != tracks.end(); ++i) {
836 boost::shared_ptr<Region> r;
839 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
840 if (rtav->track()!=0)
841 track_speed = rtav->track()->speed();
844 track_frame = session_frame_to_track_frame(frame, track_speed);
846 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
852 rpos = r->first_frame ();
856 rpos = r->last_frame ();
860 rpos = r->sync_position ();
864 // rpos is a "track frame", converting it to "_session frame"
865 rpos = track_frame_to_session_frame(rpos, track_speed);
868 distance = rpos - frame;
870 distance = frame - rpos;
873 if (distance < closest) {
885 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
887 framecnt_t distance = max_framepos;
888 framepos_t current_nearest = -1;
890 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
891 framepos_t contender;
894 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
900 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
904 d = ::llabs (pos - contender);
907 current_nearest = contender;
912 return current_nearest;
916 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
921 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
923 if (!selection->tracks.empty()) {
925 target = find_next_region_boundary (pos, dir, selection->tracks);
929 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
930 get_onscreen_tracks (tvl);
931 target = find_next_region_boundary (pos, dir, tvl);
933 target = find_next_region_boundary (pos, dir, track_views);
939 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
940 get_onscreen_tracks (tvl);
941 target = find_next_region_boundary (pos, dir, tvl);
943 target = find_next_region_boundary (pos, dir, track_views);
951 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
953 framepos_t pos = playhead_cursor->current_frame ();
960 // so we don't find the current region again..
961 if (dir > 0 || pos > 0) {
965 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
969 _session->request_locate (target);
973 Editor::cursor_to_next_region_boundary (bool with_selection)
975 cursor_to_region_boundary (with_selection, 1);
979 Editor::cursor_to_previous_region_boundary (bool with_selection)
981 cursor_to_region_boundary (with_selection, -1);
985 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
987 boost::shared_ptr<Region> r;
988 framepos_t pos = cursor->current_frame ();
994 TimeAxisView *ontrack = 0;
996 // so we don't find the current region again..
1000 if (!selection->tracks.empty()) {
1002 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1004 } else if (clicked_axisview) {
1007 t.push_back (clicked_axisview);
1009 r = find_next_region (pos, point, dir, t, &ontrack);
1013 r = find_next_region (pos, point, dir, track_views, &ontrack);
1022 pos = r->first_frame ();
1026 pos = r->last_frame ();
1030 pos = r->sync_position ();
1035 RouteTimeAxisView *rtav;
1037 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1038 if (rtav->track() != 0) {
1039 speed = rtav->track()->speed();
1043 pos = track_frame_to_session_frame(pos, speed);
1045 if (cursor == playhead_cursor) {
1046 _session->request_locate (pos);
1048 cursor->set_position (pos);
1053 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1055 cursor_to_region_point (cursor, point, 1);
1059 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1061 cursor_to_region_point (cursor, point, -1);
1065 Editor::cursor_to_selection_start (EditorCursor *cursor)
1069 switch (mouse_mode) {
1071 if (!selection->regions.empty()) {
1072 pos = selection->regions.start();
1077 if (!selection->time.empty()) {
1078 pos = selection->time.start ();
1086 if (cursor == playhead_cursor) {
1087 _session->request_locate (pos);
1089 cursor->set_position (pos);
1094 Editor::cursor_to_selection_end (EditorCursor *cursor)
1098 switch (mouse_mode) {
1100 if (!selection->regions.empty()) {
1101 pos = selection->regions.end_frame();
1106 if (!selection->time.empty()) {
1107 pos = selection->time.end_frame ();
1115 if (cursor == playhead_cursor) {
1116 _session->request_locate (pos);
1118 cursor->set_position (pos);
1123 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1133 if (selection->markers.empty()) {
1137 if (!mouse_frame (mouse, ignored)) {
1141 add_location_mark (mouse);
1144 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1148 framepos_t pos = loc->start();
1150 // so we don't find the current region again..
1151 if (dir > 0 || pos > 0) {
1155 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1159 loc->move_to (target, 0);
1163 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1165 selected_marker_to_region_boundary (with_selection, 1);
1169 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1171 selected_marker_to_region_boundary (with_selection, -1);
1175 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1177 boost::shared_ptr<Region> r;
1182 if (!_session || selection->markers.empty()) {
1186 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1190 TimeAxisView *ontrack = 0;
1194 // so we don't find the current region again..
1198 if (!selection->tracks.empty()) {
1200 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1204 r = find_next_region (pos, point, dir, track_views, &ontrack);
1213 pos = r->first_frame ();
1217 pos = r->last_frame ();
1221 pos = r->adjust_to_sync (r->first_frame());
1226 RouteTimeAxisView *rtav;
1228 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1229 if (rtav->track() != 0) {
1230 speed = rtav->track()->speed();
1234 pos = track_frame_to_session_frame(pos, speed);
1236 loc->move_to (pos, 0);
1240 Editor::selected_marker_to_next_region_point (RegionPoint point)
1242 selected_marker_to_region_point (point, 1);
1246 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1248 selected_marker_to_region_point (point, -1);
1252 Editor::selected_marker_to_selection_start ()
1258 if (!_session || selection->markers.empty()) {
1262 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1266 switch (mouse_mode) {
1268 if (!selection->regions.empty()) {
1269 pos = selection->regions.start();
1274 if (!selection->time.empty()) {
1275 pos = selection->time.start ();
1283 loc->move_to (pos, 0);
1287 Editor::selected_marker_to_selection_end ()
1293 if (!_session || selection->markers.empty()) {
1297 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1301 switch (mouse_mode) {
1303 if (!selection->regions.empty()) {
1304 pos = selection->regions.end_frame();
1309 if (!selection->time.empty()) {
1310 pos = selection->time.end_frame ();
1318 loc->move_to (pos, 0);
1322 Editor::scroll_playhead (bool forward)
1324 framepos_t pos = playhead_cursor->current_frame ();
1325 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1328 if (pos == max_framepos) {
1332 if (pos < max_framepos - delta) {
1351 _session->request_locate (pos);
1355 Editor::cursor_align (bool playhead_to_edit)
1361 if (playhead_to_edit) {
1363 if (selection->markers.empty()) {
1367 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1370 const int32_t divisions = get_grid_music_divisions (0);
1371 /* move selected markers to playhead */
1373 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1376 Location* loc = find_location_from_marker (*i, ignored);
1378 if (loc->is_mark()) {
1379 loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
1381 loc->set (playhead_cursor->current_frame (),
1382 playhead_cursor->current_frame () + loc->length(), true, divisions);
1389 Editor::scroll_backward (float pages)
1391 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1392 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1395 if (leftmost_frame < cnt) {
1398 frame = leftmost_frame - cnt;
1401 reset_x_origin (frame);
1405 Editor::scroll_forward (float pages)
1407 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1408 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1411 if (max_framepos - cnt < leftmost_frame) {
1412 frame = max_framepos - cnt;
1414 frame = leftmost_frame + cnt;
1417 reset_x_origin (frame);
1421 Editor::scroll_tracks_down ()
1423 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1424 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1425 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1428 vertical_adjustment.set_value (vert_value);
1432 Editor::scroll_tracks_up ()
1434 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1438 Editor::scroll_tracks_down_line ()
1440 double vert_value = vertical_adjustment.get_value() + 60;
1442 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1443 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1446 vertical_adjustment.set_value (vert_value);
1450 Editor::scroll_tracks_up_line ()
1452 reset_y_origin (vertical_adjustment.get_value() - 60);
1456 Editor::select_topmost_track ()
1458 const double top_of_trackviews = vertical_adjustment.get_value();
1459 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1460 if ((*t)->hidden()) {
1463 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1465 selection->set (*t);
1472 Editor::scroll_down_one_track (bool skip_child_views)
1474 TrackViewList::reverse_iterator next = track_views.rend();
1475 const double top_of_trackviews = vertical_adjustment.get_value();
1477 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1478 if ((*t)->hidden()) {
1482 /* If this is the upper-most visible trackview, we want to display
1483 * the one above it (next)
1485 * Note that covers_y_position() is recursive and includes child views
1487 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1490 if (skip_child_views) {
1493 /* automation lane (one level, non-recursive)
1495 * - if no automation lane exists -> move to next tack
1496 * - if the first (here: bottom-most) matches -> move to next tack
1497 * - if no y-axis match is found -> the current track is at the top
1498 * -> move to last (here: top-most) automation lane
1500 TimeAxisView::Children kids = (*t)->get_child_list();
1501 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1503 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1504 if ((*ci)->hidden()) {
1508 std::pair<TimeAxisView*,double> dev;
1509 dev = (*ci)->covers_y_position (top_of_trackviews);
1511 /* some automation lane is currently at the top */
1512 if (ci == kids.rbegin()) {
1513 /* first (bottom-most) autmation lane is at the top.
1514 * -> move to next track
1523 if (nkid != kids.rend()) {
1524 ensure_time_axis_view_is_visible (**nkid, true);
1532 /* move to the track below the first one that covers the */
1534 if (next != track_views.rend()) {
1535 ensure_time_axis_view_is_visible (**next, true);
1543 Editor::scroll_up_one_track (bool skip_child_views)
1545 TrackViewList::iterator prev = track_views.end();
1546 double top_of_trackviews = vertical_adjustment.get_value ();
1548 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1550 if ((*t)->hidden()) {
1554 /* find the trackview at the top of the trackview group
1556 * Note that covers_y_position() is recursive and includes child views
1558 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1561 if (skip_child_views) {
1564 /* automation lane (one level, non-recursive)
1566 * - if no automation lane exists -> move to prev tack
1567 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1568 * (actually last automation lane of previous track, see below)
1569 * - if first (top-most) lane is at the top -> move to this track
1570 * - else move up one lane
1572 TimeAxisView::Children kids = (*t)->get_child_list();
1573 TimeAxisView::Children::iterator pkid = kids.end();
1575 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1576 if ((*ci)->hidden()) {
1580 std::pair<TimeAxisView*,double> dev;
1581 dev = (*ci)->covers_y_position (top_of_trackviews);
1583 /* some automation lane is currently at the top */
1584 if (ci == kids.begin()) {
1585 /* first (top-most) autmation lane is at the top.
1586 * jump directly to this track's top
1588 ensure_time_axis_view_is_visible (**t, true);
1591 else if (pkid != kids.end()) {
1592 /* some other automation lane is at the top.
1593 * move up to prev automation lane.
1595 ensure_time_axis_view_is_visible (**pkid, true);
1598 assert(0); // not reached
1609 if (prev != track_views.end()) {
1610 // move to bottom-most automation-lane of the previous track
1611 TimeAxisView::Children kids = (*prev)->get_child_list();
1612 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1613 if (!skip_child_views) {
1614 // find the last visible lane
1615 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1616 if (!(*ci)->hidden()) {
1622 if (pkid != kids.rend()) {
1623 ensure_time_axis_view_is_visible (**pkid, true);
1625 ensure_time_axis_view_is_visible (**prev, true);
1634 Editor::scroll_left_step ()
1636 framepos_t xdelta = (current_page_samples() / 8);
1638 if (leftmost_frame > xdelta) {
1639 reset_x_origin (leftmost_frame - xdelta);
1647 Editor::scroll_right_step ()
1649 framepos_t xdelta = (current_page_samples() / 8);
1651 if (max_framepos - xdelta > leftmost_frame) {
1652 reset_x_origin (leftmost_frame + xdelta);
1654 reset_x_origin (max_framepos - current_page_samples());
1659 Editor::scroll_left_half_page ()
1661 framepos_t xdelta = (current_page_samples() / 2);
1662 if (leftmost_frame > xdelta) {
1663 reset_x_origin (leftmost_frame - xdelta);
1670 Editor::scroll_right_half_page ()
1672 framepos_t xdelta = (current_page_samples() / 2);
1673 if (max_framepos - xdelta > leftmost_frame) {
1674 reset_x_origin (leftmost_frame + xdelta);
1676 reset_x_origin (max_framepos - current_page_samples());
1683 Editor::tav_zoom_step (bool coarser)
1685 DisplaySuspender ds;
1689 if (selection->tracks.empty()) {
1692 ts = &selection->tracks;
1695 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1696 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1697 tv->step_height (coarser);
1702 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1704 DisplaySuspender ds;
1708 if (selection->tracks.empty() || force_all) {
1711 ts = &selection->tracks;
1714 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1715 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1716 uint32_t h = tv->current_height ();
1721 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1726 tv->set_height (h + 5);
1732 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1734 Editing::ZoomFocus temp_focus = zoom_focus;
1735 zoom_focus = Editing::ZoomFocusMouse;
1736 temporal_zoom_step_scale (zoom_out, scale);
1737 zoom_focus = temp_focus;
1741 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1743 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1747 Editor::temporal_zoom_step (bool zoom_out)
1749 temporal_zoom_step_scale (zoom_out, 2.0);
1753 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1755 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1757 framecnt_t nspp = samples_per_pixel;
1761 if (nspp == samples_per_pixel) {
1766 if (nspp == samples_per_pixel) {
1771 temporal_zoom (nspp);
1775 Editor::temporal_zoom (framecnt_t fpp)
1781 framepos_t current_page = current_page_samples();
1782 framepos_t current_leftmost = leftmost_frame;
1783 framepos_t current_rightmost;
1784 framepos_t current_center;
1785 framepos_t new_page_size;
1786 framepos_t half_page_size;
1787 framepos_t leftmost_after_zoom = 0;
1789 bool in_track_canvas;
1790 bool use_mouse_frame = true;
1794 if (fpp == samples_per_pixel) {
1798 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1799 // segfaults for lack of memory. If somebody decides this is not high enough I
1800 // believe it can be raisen to higher values but some limit must be in place.
1802 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1803 // all of which is used for the editor track displays. The whole day
1804 // would be 4147200000 samples, so 2592000 samples per pixel.
1806 nfpp = min (fpp, (framecnt_t) 2592000);
1807 nfpp = max ((framecnt_t) 1, nfpp);
1809 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1810 half_page_size = new_page_size / 2;
1812 switch (zoom_focus) {
1814 leftmost_after_zoom = current_leftmost;
1817 case ZoomFocusRight:
1818 current_rightmost = leftmost_frame + current_page;
1819 if (current_rightmost < new_page_size) {
1820 leftmost_after_zoom = 0;
1822 leftmost_after_zoom = current_rightmost - new_page_size;
1826 case ZoomFocusCenter:
1827 current_center = current_leftmost + (current_page/2);
1828 if (current_center < half_page_size) {
1829 leftmost_after_zoom = 0;
1831 leftmost_after_zoom = current_center - half_page_size;
1835 case ZoomFocusPlayhead:
1836 /* centre playhead */
1837 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1840 leftmost_after_zoom = 0;
1841 } else if (l > max_framepos) {
1842 leftmost_after_zoom = max_framepos - new_page_size;
1844 leftmost_after_zoom = (framepos_t) l;
1848 case ZoomFocusMouse:
1849 /* try to keep the mouse over the same point in the display */
1851 if (_drags->active()) {
1852 where = _drags->current_pointer_frame ();
1853 } else if (!mouse_frame (where, in_track_canvas)) {
1854 use_mouse_frame = false;
1857 if (use_mouse_frame) {
1858 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1861 leftmost_after_zoom = 0;
1862 } else if (l > max_framepos) {
1863 leftmost_after_zoom = max_framepos - new_page_size;
1865 leftmost_after_zoom = (framepos_t) l;
1868 /* use playhead instead */
1869 where = playhead_cursor->current_frame ();
1871 if (where < half_page_size) {
1872 leftmost_after_zoom = 0;
1874 leftmost_after_zoom = where - half_page_size;
1880 /* try to keep the edit point in the same place */
1881 where = get_preferred_edit_position ();
1885 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1888 leftmost_after_zoom = 0;
1889 } else if (l > max_framepos) {
1890 leftmost_after_zoom = max_framepos - new_page_size;
1892 leftmost_after_zoom = (framepos_t) l;
1896 /* edit point not defined */
1903 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1905 reposition_and_zoom (leftmost_after_zoom, nfpp);
1909 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1911 /* this func helps make sure we leave a little space
1912 at each end of the editor so that the zoom doesn't fit the region
1913 precisely to the screen.
1916 GdkScreen* screen = gdk_screen_get_default ();
1917 const gint pixwidth = gdk_screen_get_width (screen);
1918 const gint mmwidth = gdk_screen_get_width_mm (screen);
1919 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1920 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1922 const framepos_t range = end - start;
1923 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1924 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1926 if (start > extra_samples) {
1927 start -= extra_samples;
1932 if (max_framepos - extra_samples > end) {
1933 end += extra_samples;
1940 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1942 start = max_framepos;
1946 //ToDo: if notes are selected, set extents to that selection
1948 //ToDo: if control points are selected, set extents to that selection
1950 if ( !selection->regions.empty() ) {
1951 RegionSelection rs = get_regions_from_selection_and_entered ();
1953 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1955 if ((*i)->region()->position() < start) {
1956 start = (*i)->region()->position();
1959 if ((*i)->region()->last_frame() + 1 > end) {
1960 end = (*i)->region()->last_frame() + 1;
1964 } else if (!selection->time.empty()) {
1965 start = selection->time.start();
1966 end = selection->time.end_frame();
1968 ret = false; //no selection found
1971 if ((start == 0 && end == 0) || end < start) {
1980 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1982 if (!selection) return;
1984 //ToDo: if notes are selected, zoom to that
1986 //ToDo: if control points are selected, zoom to that
1988 if (axes == Horizontal || axes == Both) {
1990 framepos_t start, end;
1991 if (get_selection_extents (start, end)) {
1992 calc_extra_zoom_edges (start, end);
1993 temporal_zoom_by_frame (start, end);
1997 if (axes == Vertical || axes == Both) {
2003 Editor::temporal_zoom_session ()
2005 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2008 framecnt_t start = _session->current_start_frame();
2009 framecnt_t end = _session->current_end_frame();
2011 if (_session->actively_recording () ) {
2012 framepos_t cur = playhead_cursor->current_frame ();
2014 /* recording beyond the end marker; zoom out
2015 * by 5 seconds more so that if 'follow
2016 * playhead' is active we don't immediately
2019 end = cur + _session->frame_rate() * 5;
2023 if ((start == 0 && end == 0) || end < start) {
2027 calc_extra_zoom_edges(start, end);
2029 temporal_zoom_by_frame (start, end);
2034 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2036 if (!_session) return;
2038 if ((start == 0 && end == 0) || end < start) {
2042 framepos_t range = end - start;
2044 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2046 framepos_t new_page = range;
2047 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2048 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2050 if (new_leftmost > middle) {
2054 if (new_leftmost < 0) {
2058 reposition_and_zoom (new_leftmost, new_fpp);
2062 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2068 framecnt_t range_before = frame - leftmost_frame;
2072 if (samples_per_pixel <= 1) {
2075 new_spp = samples_per_pixel + (samples_per_pixel/2);
2077 range_before += range_before/2;
2079 if (samples_per_pixel >= 1) {
2080 new_spp = samples_per_pixel - (samples_per_pixel/2);
2082 /* could bail out here since we cannot zoom any finer,
2083 but leave that to the equality test below
2085 new_spp = samples_per_pixel;
2088 range_before -= range_before/2;
2091 if (new_spp == samples_per_pixel) {
2095 /* zoom focus is automatically taken as @param frame when this
2099 framepos_t new_leftmost = frame - (framepos_t)range_before;
2101 if (new_leftmost > frame) {
2105 if (new_leftmost < 0) {
2109 reposition_and_zoom (new_leftmost, new_spp);
2114 Editor::choose_new_marker_name(string &name) {
2116 if (!UIConfiguration::instance().get_name_new_markers()) {
2117 /* don't prompt user for a new name */
2121 ArdourPrompter dialog (true);
2123 dialog.set_prompt (_("New Name:"));
2125 dialog.set_title (_("New Location Marker"));
2127 dialog.set_name ("MarkNameWindow");
2128 dialog.set_size_request (250, -1);
2129 dialog.set_position (Gtk::WIN_POS_MOUSE);
2131 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2132 dialog.set_initial_text (name);
2136 switch (dialog.run ()) {
2137 case RESPONSE_ACCEPT:
2143 dialog.get_result(name);
2150 Editor::add_location_from_selection ()
2154 if (selection->time.empty()) {
2158 if (_session == 0 || clicked_axisview == 0) {
2162 framepos_t start = selection->time[clicked_selection].start;
2163 framepos_t end = selection->time[clicked_selection].end;
2165 _session->locations()->next_available_name(rangename,"selection");
2166 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
2168 begin_reversible_command (_("add marker"));
2170 XMLNode &before = _session->locations()->get_state();
2171 _session->locations()->add (location, true);
2172 XMLNode &after = _session->locations()->get_state();
2173 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2175 commit_reversible_command ();
2179 Editor::add_location_mark (framepos_t where)
2183 select_new_marker = true;
2185 _session->locations()->next_available_name(markername,"mark");
2186 if (!choose_new_marker_name(markername)) {
2189 Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
2190 begin_reversible_command (_("add marker"));
2192 XMLNode &before = _session->locations()->get_state();
2193 _session->locations()->add (location, true);
2194 XMLNode &after = _session->locations()->get_state();
2195 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2197 commit_reversible_command ();
2201 Editor::set_session_start_from_playhead ()
2207 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2208 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2210 XMLNode &before = loc->get_state();
2212 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2214 XMLNode &after = loc->get_state();
2216 begin_reversible_command (_("Set session start"));
2218 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2220 commit_reversible_command ();
2225 Editor::set_session_end_from_playhead ()
2231 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2232 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2234 XMLNode &before = loc->get_state();
2236 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2238 XMLNode &after = loc->get_state();
2240 begin_reversible_command (_("Set session start"));
2242 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2244 commit_reversible_command ();
2247 _session->set_end_is_free (false);
2252 Editor::toggle_location_at_playhead_cursor ()
2254 if (!do_remove_location_at_playhead_cursor())
2256 add_location_from_playhead_cursor();
2261 Editor::add_location_from_playhead_cursor ()
2263 add_location_mark (_session->audible_frame());
2267 Editor::do_remove_location_at_playhead_cursor ()
2269 bool removed = false;
2272 XMLNode &before = _session->locations()->get_state();
2274 //find location(s) at this time
2275 Locations::LocationList locs;
2276 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2277 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2278 if ((*i)->is_mark()) {
2279 _session->locations()->remove (*i);
2286 begin_reversible_command (_("remove marker"));
2287 XMLNode &after = _session->locations()->get_state();
2288 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2289 commit_reversible_command ();
2296 Editor::remove_location_at_playhead_cursor ()
2298 do_remove_location_at_playhead_cursor ();
2301 /** Add a range marker around each selected region */
2303 Editor::add_locations_from_region ()
2305 RegionSelection rs = get_regions_from_selection_and_entered ();
2310 bool commit = false;
2312 XMLNode &before = _session->locations()->get_state();
2314 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2316 boost::shared_ptr<Region> region = (*i)->region ();
2318 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
2320 _session->locations()->add (location, true);
2325 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2326 XMLNode &after = _session->locations()->get_state();
2327 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2328 commit_reversible_command ();
2332 /** Add a single range marker around all selected regions */
2334 Editor::add_location_from_region ()
2336 RegionSelection rs = get_regions_from_selection_and_entered ();
2342 XMLNode &before = _session->locations()->get_state();
2346 if (rs.size() > 1) {
2347 _session->locations()->next_available_name(markername, "regions");
2349 RegionView* rv = *(rs.begin());
2350 boost::shared_ptr<Region> region = rv->region();
2351 markername = region->name();
2354 if (!choose_new_marker_name(markername)) {
2358 // single range spanning all selected
2359 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
2360 _session->locations()->add (location, true);
2362 begin_reversible_command (_("add marker"));
2363 XMLNode &after = _session->locations()->get_state();
2364 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2365 commit_reversible_command ();
2371 Editor::jump_forward_to_mark ()
2377 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2383 _session->request_locate (pos, _session->transport_rolling());
2387 Editor::jump_backward_to_mark ()
2393 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2395 //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...
2396 if ( _session->transport_rolling() ) {
2397 if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
2398 framepos_t prior = _session->locations()->first_mark_before ( pos );
2407 _session->request_locate (pos, _session->transport_rolling());
2413 framepos_t const pos = _session->audible_frame ();
2416 _session->locations()->next_available_name (markername, "mark");
2418 if (!choose_new_marker_name (markername)) {
2422 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
2426 Editor::clear_markers ()
2429 begin_reversible_command (_("clear markers"));
2431 XMLNode &before = _session->locations()->get_state();
2432 _session->locations()->clear_markers ();
2433 XMLNode &after = _session->locations()->get_state();
2434 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2436 commit_reversible_command ();
2441 Editor::clear_ranges ()
2444 begin_reversible_command (_("clear ranges"));
2446 XMLNode &before = _session->locations()->get_state();
2448 _session->locations()->clear_ranges ();
2450 XMLNode &after = _session->locations()->get_state();
2451 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2453 commit_reversible_command ();
2458 Editor::clear_locations ()
2460 begin_reversible_command (_("clear locations"));
2462 XMLNode &before = _session->locations()->get_state();
2463 _session->locations()->clear ();
2464 XMLNode &after = _session->locations()->get_state();
2465 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2467 commit_reversible_command ();
2471 Editor::unhide_markers ()
2473 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2474 Location *l = (*i).first;
2475 if (l->is_hidden() && l->is_mark()) {
2476 l->set_hidden(false, this);
2482 Editor::unhide_ranges ()
2484 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2485 Location *l = (*i).first;
2486 if (l->is_hidden() && l->is_range_marker()) {
2487 l->set_hidden(false, this);
2492 /* INSERT/REPLACE */
2495 Editor::insert_region_list_selection (float times)
2497 RouteTimeAxisView *tv = 0;
2498 boost::shared_ptr<Playlist> playlist;
2500 if (clicked_routeview != 0) {
2501 tv = clicked_routeview;
2502 } else if (!selection->tracks.empty()) {
2503 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2506 } else if (entered_track != 0) {
2507 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2514 if ((playlist = tv->playlist()) == 0) {
2518 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2523 begin_reversible_command (_("insert region"));
2524 playlist->clear_changes ();
2525 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2526 if (Config->get_edit_mode() == Ripple)
2527 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2529 _session->add_command(new StatefulDiffCommand (playlist));
2530 commit_reversible_command ();
2533 /* BUILT-IN EFFECTS */
2536 Editor::reverse_selection ()
2541 /* GAIN ENVELOPE EDITING */
2544 Editor::edit_envelope ()
2551 Editor::transition_to_rolling (bool fwd)
2557 if (_session->config.get_external_sync()) {
2558 switch (Config->get_sync_source()) {
2562 /* transport controlled by the master */
2567 if (_session->is_auditioning()) {
2568 _session->cancel_audition ();
2572 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2576 Editor::play_from_start ()
2578 _session->request_locate (_session->current_start_frame(), true);
2582 Editor::play_from_edit_point ()
2584 _session->request_locate (get_preferred_edit_position(), true);
2588 Editor::play_from_edit_point_and_return ()
2590 framepos_t start_frame;
2591 framepos_t return_frame;
2593 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2595 if (_session->transport_rolling()) {
2596 _session->request_locate (start_frame, false);
2600 /* don't reset the return frame if its already set */
2602 if ((return_frame = _session->requested_return_frame()) < 0) {
2603 return_frame = _session->audible_frame();
2606 if (start_frame >= 0) {
2607 _session->request_roll_at_and_return (start_frame, return_frame);
2612 Editor::play_selection ()
2614 framepos_t start, end;
2615 if (!get_selection_extents ( start, end))
2618 AudioRange ar (start, end, 0);
2619 list<AudioRange> lar;
2622 _session->request_play_range (&lar, true);
2627 Editor::maybe_locate_with_edit_preroll (framepos_t location)
2629 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
2632 location -= _session->preroll_samples (location);
2634 //don't try to locate before the beginning of time
2639 //if follow_playhead is on, keep the playhead on the screen
2640 if ( _follow_playhead )
2641 if ( location < leftmost_frame )
2642 location = leftmost_frame;
2644 _session->request_locate( location );
2648 Editor::play_with_preroll ()
2650 framepos_t start, end;
2651 if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
2652 const framepos_t preroll = _session->preroll_samples (start);
2654 framepos_t ret = start;
2656 if (start > preroll) {
2657 start = start - preroll;
2660 end = end + preroll; //"post-roll"
2662 AudioRange ar (start, end, 0);
2663 list<AudioRange> lar;
2666 _session->request_play_range (&lar, true);
2667 _session->set_requested_return_frame (ret); //force auto-return to return to range start, without the preroll
2669 framepos_t ph = playhead_cursor->current_frame ();
2670 const framepos_t preroll = _session->preroll_samples (ph);
2673 start = ph - preroll;
2677 _session->request_locate (start, true);
2678 _session->set_requested_return_frame (ph); //force auto-return to return to playhead location, without the preroll
2683 Editor::rec_with_preroll ()
2685 framepos_t ph = playhead_cursor->current_frame ();
2686 framepos_t preroll = _session->preroll_samples (ph);
2687 _session->request_preroll_record_trim (ph, preroll);
2691 Editor::rec_with_count_in ()
2693 _session->request_count_in_record ();
2697 Editor::play_location (Location& location)
2699 if (location.start() <= location.end()) {
2703 _session->request_bounded_roll (location.start(), location.end());
2707 Editor::loop_location (Location& location)
2709 if (location.start() <= location.end()) {
2715 if ((tll = transport_loop_location()) != 0) {
2716 tll->set (location.start(), location.end());
2718 // enable looping, reposition and start rolling
2719 _session->request_locate (tll->start(), true);
2720 _session->request_play_loop (true);
2725 Editor::do_layer_operation (LayerOperation op)
2727 if (selection->regions.empty ()) {
2731 bool const multiple = selection->regions.size() > 1;
2735 begin_reversible_command (_("raise regions"));
2737 begin_reversible_command (_("raise region"));
2743 begin_reversible_command (_("raise regions to top"));
2745 begin_reversible_command (_("raise region to top"));
2751 begin_reversible_command (_("lower regions"));
2753 begin_reversible_command (_("lower region"));
2759 begin_reversible_command (_("lower regions to bottom"));
2761 begin_reversible_command (_("lower region"));
2766 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2767 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2768 (*i)->clear_owned_changes ();
2771 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2772 boost::shared_ptr<Region> r = (*i)->region ();
2784 r->lower_to_bottom ();
2788 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2789 vector<Command*> cmds;
2791 _session->add_commands (cmds);
2794 commit_reversible_command ();
2798 Editor::raise_region ()
2800 do_layer_operation (Raise);
2804 Editor::raise_region_to_top ()
2806 do_layer_operation (RaiseToTop);
2810 Editor::lower_region ()
2812 do_layer_operation (Lower);
2816 Editor::lower_region_to_bottom ()
2818 do_layer_operation (LowerToBottom);
2821 /** Show the region editor for the selected regions */
2823 Editor::show_region_properties ()
2825 selection->foreach_regionview (&RegionView::show_region_editor);
2828 /** Show the midi list editor for the selected MIDI regions */
2830 Editor::show_midi_list_editor ()
2832 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2836 Editor::rename_region ()
2838 RegionSelection rs = get_regions_from_selection_and_entered ();
2844 ArdourDialog d (_("Rename Region"), true, false);
2846 Label label (_("New name:"));
2849 hbox.set_spacing (6);
2850 hbox.pack_start (label, false, false);
2851 hbox.pack_start (entry, true, true);
2853 d.get_vbox()->set_border_width (12);
2854 d.get_vbox()->pack_start (hbox, false, false);
2856 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2857 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2859 d.set_size_request (300, -1);
2861 entry.set_text (rs.front()->region()->name());
2862 entry.select_region (0, -1);
2864 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2870 int const ret = d.run();
2874 if (ret != RESPONSE_OK) {
2878 std::string str = entry.get_text();
2879 strip_whitespace_edges (str);
2881 rs.front()->region()->set_name (str);
2882 _regions->redisplay ();
2886 /** Start an audition of the first selected region */
2888 Editor::play_edit_range ()
2890 framepos_t start, end;
2892 if (get_edit_op_range (start, end)) {
2893 _session->request_bounded_roll (start, end);
2898 Editor::play_selected_region ()
2900 framepos_t start = max_framepos;
2903 RegionSelection rs = get_regions_from_selection_and_entered ();
2909 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2910 if ((*i)->region()->position() < start) {
2911 start = (*i)->region()->position();
2913 if ((*i)->region()->last_frame() + 1 > end) {
2914 end = (*i)->region()->last_frame() + 1;
2918 _session->request_bounded_roll (start, end);
2922 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2924 _session->audition_region (region);
2928 Editor::region_from_selection ()
2930 if (clicked_axisview == 0) {
2934 if (selection->time.empty()) {
2938 framepos_t start = selection->time[clicked_selection].start;
2939 framepos_t end = selection->time[clicked_selection].end;
2941 TrackViewList tracks = get_tracks_for_range_action ();
2943 framepos_t selection_cnt = end - start + 1;
2945 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2946 boost::shared_ptr<Region> current;
2947 boost::shared_ptr<Playlist> pl;
2948 framepos_t internal_start;
2951 if ((pl = (*i)->playlist()) == 0) {
2955 if ((current = pl->top_region_at (start)) == 0) {
2959 internal_start = start - current->position();
2960 RegionFactory::region_name (new_name, current->name(), true);
2964 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2965 plist.add (ARDOUR::Properties::length, selection_cnt);
2966 plist.add (ARDOUR::Properties::name, new_name);
2967 plist.add (ARDOUR::Properties::layer, 0);
2969 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2974 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2976 if (selection->time.empty() || selection->tracks.empty()) {
2980 framepos_t start, end;
2981 if (clicked_selection) {
2982 start = selection->time[clicked_selection].start;
2983 end = selection->time[clicked_selection].end;
2985 start = selection->time.start();
2986 end = selection->time.end_frame();
2989 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2990 sort_track_selection (ts);
2992 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2993 boost::shared_ptr<Region> current;
2994 boost::shared_ptr<Playlist> playlist;
2995 framepos_t internal_start;
2998 if ((playlist = (*i)->playlist()) == 0) {
3002 if ((current = playlist->top_region_at(start)) == 0) {
3006 internal_start = start - current->position();
3007 RegionFactory::region_name (new_name, current->name(), true);
3011 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3012 plist.add (ARDOUR::Properties::length, end - start + 1);
3013 plist.add (ARDOUR::Properties::name, new_name);
3015 new_regions.push_back (RegionFactory::create (current, plist));
3020 Editor::split_multichannel_region ()
3022 RegionSelection rs = get_regions_from_selection_and_entered ();
3028 vector< boost::shared_ptr<Region> > v;
3030 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3031 (*x)->region()->separate_by_channel (*_session, v);
3036 Editor::new_region_from_selection ()
3038 region_from_selection ();
3039 cancel_selection ();
3043 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3045 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3046 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3047 case Evoral::OverlapNone:
3055 * - selected tracks, or if there are none...
3056 * - tracks containing selected regions, or if there are none...
3061 Editor::get_tracks_for_range_action () const
3065 if (selection->tracks.empty()) {
3067 /* use tracks with selected regions */
3069 RegionSelection rs = selection->regions;
3071 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3072 TimeAxisView* tv = &(*i)->get_time_axis_view();
3074 if (!t.contains (tv)) {
3080 /* no regions and no tracks: use all tracks */
3086 t = selection->tracks;
3089 return t.filter_to_unique_playlists();
3093 Editor::separate_regions_between (const TimeSelection& ts)
3095 bool in_command = false;
3096 boost::shared_ptr<Playlist> playlist;
3097 RegionSelection new_selection;
3099 TrackViewList tmptracks = get_tracks_for_range_action ();
3100 sort_track_selection (tmptracks);
3102 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3104 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3110 if (!rtv->is_track()) {
3114 /* no edits to destructive tracks */
3116 if (rtv->track()->destructive()) {
3120 if ((playlist = rtv->playlist()) != 0) {
3122 playlist->clear_changes ();
3124 /* XXX need to consider musical time selections here at some point */
3126 double speed = rtv->track()->speed();
3128 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3130 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3131 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3133 latest_regionviews.clear ();
3135 playlist->partition ((framepos_t)((*t).start * speed),
3136 (framepos_t)((*t).end * speed), false);
3140 if (!latest_regionviews.empty()) {
3142 rtv->view()->foreach_regionview (sigc::bind (
3143 sigc::ptr_fun (add_if_covered),
3144 &(*t), &new_selection));
3147 begin_reversible_command (_("separate"));
3151 /* pick up changes to existing regions */
3153 vector<Command*> cmds;
3154 playlist->rdiff (cmds);
3155 _session->add_commands (cmds);
3157 /* pick up changes to the playlist itself (adds/removes)
3160 _session->add_command(new StatefulDiffCommand (playlist));
3167 // selection->set (new_selection);
3169 commit_reversible_command ();
3173 struct PlaylistState {
3174 boost::shared_ptr<Playlist> playlist;
3178 /** Take tracks from get_tracks_for_range_action and cut any regions
3179 * on those tracks so that the tracks are empty over the time
3183 Editor::separate_region_from_selection ()
3185 /* preferentially use *all* ranges in the time selection if we're in range mode
3186 to allow discontiguous operation, since get_edit_op_range() currently
3187 returns a single range.
3190 if (!selection->time.empty()) {
3192 separate_regions_between (selection->time);
3199 if (get_edit_op_range (start, end)) {
3201 AudioRange ar (start, end, 1);
3205 separate_regions_between (ts);
3211 Editor::separate_region_from_punch ()
3213 Location* loc = _session->locations()->auto_punch_location();
3215 separate_regions_using_location (*loc);
3220 Editor::separate_region_from_loop ()
3222 Location* loc = _session->locations()->auto_loop_location();
3224 separate_regions_using_location (*loc);
3229 Editor::separate_regions_using_location (Location& loc)
3231 if (loc.is_mark()) {
3235 AudioRange ar (loc.start(), loc.end(), 1);
3240 separate_regions_between (ts);
3243 /** Separate regions under the selected region */
3245 Editor::separate_under_selected_regions ()
3247 vector<PlaylistState> playlists;
3251 rs = get_regions_from_selection_and_entered();
3253 if (!_session || rs.empty()) {
3257 begin_reversible_command (_("separate region under"));
3259 list<boost::shared_ptr<Region> > regions_to_remove;
3261 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3262 // we can't just remove the region(s) in this loop because
3263 // this removes them from the RegionSelection, and they thus
3264 // disappear from underneath the iterator, and the ++i above
3265 // SEGVs in a puzzling fashion.
3267 // so, first iterate over the regions to be removed from rs and
3268 // add them to the regions_to_remove list, and then
3269 // iterate over the list to actually remove them.
3271 regions_to_remove.push_back ((*i)->region());
3274 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3276 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3279 // is this check necessary?
3283 vector<PlaylistState>::iterator i;
3285 //only take state if this is a new playlist.
3286 for (i = playlists.begin(); i != playlists.end(); ++i) {
3287 if ((*i).playlist == playlist) {
3292 if (i == playlists.end()) {
3294 PlaylistState before;
3295 before.playlist = playlist;
3296 before.before = &playlist->get_state();
3298 playlist->freeze ();
3299 playlists.push_back(before);
3302 //Partition on the region bounds
3303 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3305 //Re-add region that was just removed due to the partition operation
3306 playlist->add_region( (*rl), (*rl)->first_frame() );
3309 vector<PlaylistState>::iterator pl;
3311 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3312 (*pl).playlist->thaw ();
3313 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3316 commit_reversible_command ();
3320 Editor::crop_region_to_selection ()
3322 if (!selection->time.empty()) {
3324 crop_region_to (selection->time.start(), selection->time.end_frame());
3331 if (get_edit_op_range (start, end)) {
3332 crop_region_to (start, end);
3339 Editor::crop_region_to (framepos_t start, framepos_t end)
3341 vector<boost::shared_ptr<Playlist> > playlists;
3342 boost::shared_ptr<Playlist> playlist;
3345 if (selection->tracks.empty()) {
3346 ts = track_views.filter_to_unique_playlists();
3348 ts = selection->tracks.filter_to_unique_playlists ();
3351 sort_track_selection (ts);
3353 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3355 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3361 boost::shared_ptr<Track> t = rtv->track();
3363 if (t != 0 && ! t->destructive()) {
3365 if ((playlist = rtv->playlist()) != 0) {
3366 playlists.push_back (playlist);
3371 if (playlists.empty()) {
3376 framepos_t new_start;
3378 framecnt_t new_length;
3379 bool in_command = false;
3381 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3383 /* Only the top regions at start and end have to be cropped */
3384 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3385 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3387 vector<boost::shared_ptr<Region> > regions;
3389 if (region_at_start != 0) {
3390 regions.push_back (region_at_start);
3392 if (region_at_end != 0) {
3393 regions.push_back (region_at_end);
3396 /* now adjust lengths */
3397 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3399 pos = (*i)->position();
3400 new_start = max (start, pos);
3401 if (max_framepos - pos > (*i)->length()) {
3402 new_end = pos + (*i)->length() - 1;
3404 new_end = max_framepos;
3406 new_end = min (end, new_end);
3407 new_length = new_end - new_start + 1;
3410 begin_reversible_command (_("trim to selection"));
3413 (*i)->clear_changes ();
3414 (*i)->trim_to (new_start, new_length);
3415 _session->add_command (new StatefulDiffCommand (*i));
3420 commit_reversible_command ();
3425 Editor::region_fill_track ()
3427 boost::shared_ptr<Playlist> playlist;
3428 RegionSelection regions = get_regions_from_selection_and_entered ();
3429 RegionSelection foo;
3431 framepos_t const end = _session->current_end_frame ();
3433 if (regions.empty () || regions.end_frame () + 1 >= end) {
3437 framepos_t const start_frame = regions.start ();
3438 framepos_t const end_frame = regions.end_frame ();
3439 framecnt_t const gap = end_frame - start_frame + 1;
3441 begin_reversible_command (Operations::region_fill);
3443 selection->clear_regions ();
3445 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3447 boost::shared_ptr<Region> r ((*i)->region());
3449 TimeAxisView& tv = (*i)->get_time_axis_view();
3450 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3451 latest_regionviews.clear ();
3452 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3454 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3455 playlist = (*i)->region()->playlist();
3456 playlist->clear_changes ();
3457 playlist->duplicate_until (r, position, gap, end);
3458 _session->add_command(new StatefulDiffCommand (playlist));
3462 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3466 selection->set (foo);
3469 commit_reversible_command ();
3473 Editor::set_region_sync_position ()
3475 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3479 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3481 bool in_command = false;
3483 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3485 if (!(*r)->region()->covers (where)) {
3489 boost::shared_ptr<Region> region ((*r)->region());
3492 begin_reversible_command (_("set sync point"));
3496 region->clear_changes ();
3497 region->set_sync_position (where);
3498 _session->add_command(new StatefulDiffCommand (region));
3502 commit_reversible_command ();
3506 /** Remove the sync positions of the selection */
3508 Editor::remove_region_sync ()
3510 RegionSelection rs = get_regions_from_selection_and_entered ();
3516 begin_reversible_command (_("remove region sync"));
3518 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3520 (*i)->region()->clear_changes ();
3521 (*i)->region()->clear_sync_position ();
3522 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3525 commit_reversible_command ();
3529 Editor::naturalize_region ()
3531 RegionSelection rs = get_regions_from_selection_and_entered ();
3537 if (rs.size() > 1) {
3538 begin_reversible_command (_("move regions to original position"));
3540 begin_reversible_command (_("move region to original position"));
3543 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3544 (*i)->region()->clear_changes ();
3545 (*i)->region()->move_to_natural_position ();
3546 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3549 commit_reversible_command ();
3553 Editor::align_regions (RegionPoint what)
3555 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3561 begin_reversible_command (_("align selection"));
3563 framepos_t const position = get_preferred_edit_position ();
3565 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3566 align_region_internal ((*i)->region(), what, position);
3569 commit_reversible_command ();
3572 struct RegionSortByTime {
3573 bool operator() (const RegionView* a, const RegionView* b) {
3574 return a->region()->position() < b->region()->position();
3579 Editor::align_regions_relative (RegionPoint point)
3581 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3587 framepos_t const position = get_preferred_edit_position ();
3589 framepos_t distance = 0;
3593 list<RegionView*> sorted;
3594 rs.by_position (sorted);
3596 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3601 if (position > r->position()) {
3602 distance = position - r->position();
3604 distance = r->position() - position;
3610 if (position > r->last_frame()) {
3611 distance = position - r->last_frame();
3612 pos = r->position() + distance;
3614 distance = r->last_frame() - position;
3615 pos = r->position() - distance;
3621 pos = r->adjust_to_sync (position);
3622 if (pos > r->position()) {
3623 distance = pos - r->position();
3625 distance = r->position() - pos;
3631 if (pos == r->position()) {
3635 begin_reversible_command (_("align selection (relative)"));
3637 /* move first one specially */
3639 r->clear_changes ();
3640 r->set_position (pos);
3641 _session->add_command(new StatefulDiffCommand (r));
3643 /* move rest by the same amount */
3647 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3649 boost::shared_ptr<Region> region ((*i)->region());
3651 region->clear_changes ();
3654 region->set_position (region->position() + distance);
3656 region->set_position (region->position() - distance);
3659 _session->add_command(new StatefulDiffCommand (region));
3663 commit_reversible_command ();
3667 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3669 begin_reversible_command (_("align region"));
3670 align_region_internal (region, point, position);
3671 commit_reversible_command ();
3675 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3677 region->clear_changes ();
3681 region->set_position (region->adjust_to_sync (position));
3685 if (position > region->length()) {
3686 region->set_position (position - region->length());
3691 region->set_position (position);
3695 _session->add_command(new StatefulDiffCommand (region));
3699 Editor::trim_region_front ()
3705 Editor::trim_region_back ()
3707 trim_region (false);
3711 Editor::trim_region (bool front)
3713 framepos_t where = get_preferred_edit_position();
3714 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3720 begin_reversible_command (front ? _("trim front") : _("trim back"));
3722 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3723 if (!(*i)->region()->locked()) {
3725 (*i)->region()->clear_changes ();
3728 (*i)->region()->trim_front (where);
3730 (*i)->region()->trim_end (where);
3733 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3737 commit_reversible_command ();
3740 /** Trim the end of the selected regions to the position of the edit cursor */
3742 Editor::trim_region_to_loop ()
3744 Location* loc = _session->locations()->auto_loop_location();
3748 trim_region_to_location (*loc, _("trim to loop"));
3752 Editor::trim_region_to_punch ()
3754 Location* loc = _session->locations()->auto_punch_location();
3758 trim_region_to_location (*loc, _("trim to punch"));
3762 Editor::trim_region_to_location (const Location& loc, const char* str)
3764 RegionSelection rs = get_regions_from_selection_and_entered ();
3765 bool in_command = false;
3767 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3768 RegionView* rv = (*x);
3770 /* require region to span proposed trim */
3771 switch (rv->region()->coverage (loc.start(), loc.end())) {
3772 case Evoral::OverlapInternal:
3778 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3787 if (tav->track() != 0) {
3788 speed = tav->track()->speed();
3791 start = session_frame_to_track_frame (loc.start(), speed);
3792 end = session_frame_to_track_frame (loc.end(), speed);
3794 rv->region()->clear_changes ();
3795 rv->region()->trim_to (start, (end - start));
3798 begin_reversible_command (str);
3801 _session->add_command(new StatefulDiffCommand (rv->region()));
3805 commit_reversible_command ();
3810 Editor::trim_region_to_previous_region_end ()
3812 return trim_to_region(false);
3816 Editor::trim_region_to_next_region_start ()
3818 return trim_to_region(true);
3822 Editor::trim_to_region(bool forward)
3824 RegionSelection rs = get_regions_from_selection_and_entered ();
3825 bool in_command = false;
3827 boost::shared_ptr<Region> next_region;
3829 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3831 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3837 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3845 if (atav->track() != 0) {
3846 speed = atav->track()->speed();
3850 boost::shared_ptr<Region> region = arv->region();
3851 boost::shared_ptr<Playlist> playlist (region->playlist());
3853 region->clear_changes ();
3857 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3863 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3864 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3868 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3874 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3876 arv->region_changed (ARDOUR::bounds_change);
3880 begin_reversible_command (_("trim to region"));
3883 _session->add_command(new StatefulDiffCommand (region));
3887 commit_reversible_command ();
3892 Editor::unfreeze_route ()
3894 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3898 clicked_routeview->track()->unfreeze ();
3902 Editor::_freeze_thread (void* arg)
3904 return static_cast<Editor*>(arg)->freeze_thread ();
3908 Editor::freeze_thread ()
3910 /* create event pool because we may need to talk to the session */
3911 SessionEvent::create_per_thread_pool ("freeze events", 64);
3912 /* create per-thread buffers for process() tree to use */
3913 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3914 current_interthread_info->done = true;
3919 Editor::freeze_route ()
3925 /* stop transport before we start. this is important */
3927 _session->request_transport_speed (0.0);
3929 /* wait for just a little while, because the above call is asynchronous */
3931 Glib::usleep (250000);
3933 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3937 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3939 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3940 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3942 d.set_title (_("Cannot freeze"));
3947 if (clicked_routeview->track()->has_external_redirects()) {
3948 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"
3949 "Freezing will only process the signal as far as the first send/insert/return."),
3950 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3952 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3953 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3954 d.set_title (_("Freeze Limits"));
3956 int response = d.run ();
3959 case Gtk::RESPONSE_CANCEL:
3966 InterThreadInfo itt;
3967 current_interthread_info = &itt;
3969 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3971 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3973 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3975 while (!itt.done && !itt.cancel) {
3976 gtk_main_iteration ();
3979 pthread_join (itt.thread, 0);
3980 current_interthread_info = 0;
3984 Editor::bounce_range_selection (bool replace, bool enable_processing)
3986 if (selection->time.empty()) {
3990 TrackSelection views = selection->tracks;
3992 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3994 if (enable_processing) {
3996 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3998 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4000 _("You can't perform this operation because the processing of the signal "
4001 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4002 "You can do this without processing, which is a different operation.")
4004 d.set_title (_("Cannot bounce"));
4011 framepos_t start = selection->time[clicked_selection].start;
4012 framepos_t end = selection->time[clicked_selection].end;
4013 framepos_t cnt = end - start + 1;
4014 bool in_command = false;
4016 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4018 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4024 boost::shared_ptr<Playlist> playlist;
4026 if ((playlist = rtv->playlist()) == 0) {
4030 InterThreadInfo itt;
4032 playlist->clear_changes ();
4033 playlist->clear_owned_changes ();
4035 boost::shared_ptr<Region> r;
4037 if (enable_processing) {
4038 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4040 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4048 list<AudioRange> ranges;
4049 ranges.push_back (AudioRange (start, start+cnt, 0));
4050 playlist->cut (ranges); // discard result
4051 playlist->add_region (r, start);
4055 begin_reversible_command (_("bounce range"));
4058 vector<Command*> cmds;
4059 playlist->rdiff (cmds);
4060 _session->add_commands (cmds);
4062 _session->add_command (new StatefulDiffCommand (playlist));
4066 commit_reversible_command ();
4070 /** Delete selected regions, automation points or a time range */
4074 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4075 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4076 bool deleted = false;
4077 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4078 deleted = current_mixer_strip->delete_processors ();
4084 /** Cut selected regions, automation points or a time range */
4091 /** Copy selected regions, automation points or a time range */
4099 /** @return true if a Cut, Copy or Clear is possible */
4101 Editor::can_cut_copy () const
4103 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4110 /** Cut, copy or clear selected regions, automation points or a time range.
4111 * @param op Operation (Delete, Cut, Copy or Clear)
4114 Editor::cut_copy (CutCopyOp op)
4116 /* only cancel selection if cut/copy is successful.*/
4122 opname = _("delete");
4131 opname = _("clear");
4135 /* if we're deleting something, and the mouse is still pressed,
4136 the thing we started a drag for will be gone when we release
4137 the mouse button(s). avoid this. see part 2 at the end of
4141 if (op == Delete || op == Cut || op == Clear) {
4142 if (_drags->active ()) {
4147 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4148 cut_buffer->clear ();
4150 if (entered_marker) {
4152 /* cut/delete op while pointing at a marker */
4155 Location* loc = find_location_from_marker (entered_marker, ignored);
4157 if (_session && loc) {
4158 entered_marker = NULL;
4159 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4166 switch (mouse_mode) {
4169 begin_reversible_command (opname + ' ' + X_("MIDI"));
4171 commit_reversible_command ();
4177 bool did_edit = false;
4179 if (!selection->regions.empty() || !selection->points.empty()) {
4180 begin_reversible_command (opname + ' ' + _("objects"));
4183 if (!selection->regions.empty()) {
4184 cut_copy_regions (op, selection->regions);
4186 if (op == Cut || op == Delete) {
4187 selection->clear_regions ();
4191 if (!selection->points.empty()) {
4192 cut_copy_points (op);
4194 if (op == Cut || op == Delete) {
4195 selection->clear_points ();
4198 } else if (selection->time.empty()) {
4199 framepos_t start, end;
4200 /* no time selection, see if we can get an edit range
4203 if (get_edit_op_range (start, end)) {
4204 selection->set (start, end);
4206 } else if (!selection->time.empty()) {
4207 begin_reversible_command (opname + ' ' + _("range"));
4210 cut_copy_ranges (op);
4212 if (op == Cut || op == Delete) {
4213 selection->clear_time ();
4218 /* reset repeated paste state */
4221 commit_reversible_command ();
4224 if (op == Delete || op == Cut || op == Clear) {
4230 struct AutomationRecord {
4231 AutomationRecord () : state (0) , line(NULL) {}
4232 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4234 XMLNode* state; ///< state before any operation
4235 const AutomationLine* line; ///< line this came from
4236 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4239 struct PointsSelectionPositionSorter {
4240 bool operator() (ControlPoint* a, ControlPoint* b) {
4241 return (*(a->model()))->when < (*(b->model()))->when;
4245 /** Cut, copy or clear selected automation points.
4246 * @param op Operation (Cut, Copy or Clear)
4249 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4251 if (selection->points.empty ()) {
4255 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4256 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4258 /* Keep a record of the AutomationLists that we end up using in this operation */
4259 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4262 /* user could select points in any order */
4263 selection->points.sort(PointsSelectionPositionSorter ());
4265 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4266 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4267 const AutomationLine& line = (*sel_point)->line();
4268 const boost::shared_ptr<AutomationList> al = line.the_list();
4269 if (lists.find (al) == lists.end ()) {
4270 /* We haven't seen this list yet, so make a record for it. This includes
4271 taking a copy of its current state, in case this is needed for undo later.
4273 lists[al] = AutomationRecord (&al->get_state (), &line);
4277 if (op == Cut || op == Copy) {
4278 /* This operation will involve putting things in the cut buffer, so create an empty
4279 ControlList for each of our source lists to put the cut buffer data in.
4281 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4282 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4285 /* Add all selected points to the relevant copy ControlLists */
4286 MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
4287 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4288 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4289 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4291 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4293 /* Update earliest MIDI start time in beats */
4294 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4296 /* Update earliest session start time in frames */
4297 start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
4301 /* Snap start time backwards, so copy/paste is snap aligned. */
4303 if (earliest == Evoral::Beats::max()) {
4304 earliest = Evoral::Beats(); // Weird... don't offset
4306 earliest.round_down_to_beat();
4308 if (start.frame == std::numeric_limits<double>::max()) {
4309 start.frame = 0; // Weird... don't offset
4311 snap_to(start, RoundDownMaybe);
4314 const double line_offset = midi ? earliest.to_double() : start.frame;
4315 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4316 /* Correct this copy list so that it is relative to the earliest
4317 start time, so relative ordering between points is preserved
4318 when copying from several lists and the paste starts at the
4319 earliest copied piece of data. */
4320 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4321 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4322 (*ctrl_evt)->when -= line_offset;
4325 /* And add it to the cut buffer */
4326 cut_buffer->add (al_cpy);
4330 if (op == Delete || op == Cut) {
4331 /* This operation needs to remove things from the main AutomationList, so do that now */
4333 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4334 i->first->freeze ();
4337 /* Remove each selected point from its AutomationList */
4338 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4339 AutomationLine& line = (*sel_point)->line ();
4340 boost::shared_ptr<AutomationList> al = line.the_list();
4344 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4345 /* removing of first and last gain point in region gain lines is prohibited*/
4346 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4352 al->erase ((*sel_point)->model ());
4356 /* Thaw the lists and add undo records for them */
4357 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4358 boost::shared_ptr<AutomationList> al = i->first;
4360 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4365 /** Cut, copy or clear selected automation points.
4366 * @param op Operation (Cut, Copy or Clear)
4369 Editor::cut_copy_midi (CutCopyOp op)
4371 Evoral::Beats earliest = Evoral::Beats::max();
4372 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4373 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4375 if (!mrv->selection().empty()) {
4376 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4378 mrv->cut_copy_clear (op);
4380 /* XXX: not ideal, as there may be more than one track involved in the selection */
4381 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4385 if (!selection->points.empty()) {
4386 cut_copy_points (op, earliest, true);
4387 if (op == Cut || op == Delete) {
4388 selection->clear_points ();
4393 struct lt_playlist {
4394 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4395 return a.playlist < b.playlist;
4399 struct PlaylistMapping {
4401 boost::shared_ptr<Playlist> pl;
4403 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4406 /** Remove `clicked_regionview' */
4408 Editor::remove_clicked_region ()
4410 if (clicked_routeview == 0 || clicked_regionview == 0) {
4414 begin_reversible_command (_("remove region"));
4416 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4418 playlist->clear_changes ();
4419 playlist->clear_owned_changes ();
4420 playlist->remove_region (clicked_regionview->region());
4421 if (Config->get_edit_mode() == Ripple)
4422 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4424 /* We might have removed regions, which alters other regions' layering_index,
4425 so we need to do a recursive diff here.
4427 vector<Command*> cmds;
4428 playlist->rdiff (cmds);
4429 _session->add_commands (cmds);
4431 _session->add_command(new StatefulDiffCommand (playlist));
4432 commit_reversible_command ();
4436 /** Remove the selected regions */
4438 Editor::remove_selected_regions ()
4440 RegionSelection rs = get_regions_from_selection_and_entered ();
4442 if (!_session || rs.empty()) {
4446 list<boost::shared_ptr<Region> > regions_to_remove;
4448 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4449 // we can't just remove the region(s) in this loop because
4450 // this removes them from the RegionSelection, and they thus
4451 // disappear from underneath the iterator, and the ++i above
4452 // SEGVs in a puzzling fashion.
4454 // so, first iterate over the regions to be removed from rs and
4455 // add them to the regions_to_remove list, and then
4456 // iterate over the list to actually remove them.
4458 regions_to_remove.push_back ((*i)->region());
4461 vector<boost::shared_ptr<Playlist> > playlists;
4463 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4465 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4468 // is this check necessary?
4472 /* get_regions_from_selection_and_entered() guarantees that
4473 the playlists involved are unique, so there is no need
4477 playlists.push_back (playlist);
4479 playlist->clear_changes ();
4480 playlist->clear_owned_changes ();
4481 playlist->freeze ();
4482 playlist->remove_region (*rl);
4483 if (Config->get_edit_mode() == Ripple)
4484 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4488 vector<boost::shared_ptr<Playlist> >::iterator pl;
4489 bool in_command = false;
4491 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4494 /* We might have removed regions, which alters other regions' layering_index,
4495 so we need to do a recursive diff here.
4499 begin_reversible_command (_("remove region"));
4502 vector<Command*> cmds;
4503 (*pl)->rdiff (cmds);
4504 _session->add_commands (cmds);
4506 _session->add_command(new StatefulDiffCommand (*pl));
4510 commit_reversible_command ();
4514 /** Cut, copy or clear selected regions.
4515 * @param op Operation (Cut, Copy or Clear)
4518 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4520 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4521 a map when we want ordered access to both elements. i think.
4524 vector<PlaylistMapping> pmap;
4526 framepos_t first_position = max_framepos;
4528 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4529 FreezeList freezelist;
4531 /* get ordering correct before we cut/copy */
4533 rs.sort_by_position_and_track ();
4535 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4537 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4539 if (op == Cut || op == Clear || op == Delete) {
4540 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4543 FreezeList::iterator fl;
4545 // only take state if this is a new playlist.
4546 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4552 if (fl == freezelist.end()) {
4553 pl->clear_changes();
4554 pl->clear_owned_changes ();
4556 freezelist.insert (pl);
4561 TimeAxisView* tv = &(*x)->get_time_axis_view();
4562 vector<PlaylistMapping>::iterator z;
4564 for (z = pmap.begin(); z != pmap.end(); ++z) {
4565 if ((*z).tv == tv) {
4570 if (z == pmap.end()) {
4571 pmap.push_back (PlaylistMapping (tv));
4575 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4577 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4580 /* region not yet associated with a playlist (e.g. unfinished
4587 TimeAxisView& tv = (*x)->get_time_axis_view();
4588 boost::shared_ptr<Playlist> npl;
4589 RegionSelection::iterator tmp;
4596 vector<PlaylistMapping>::iterator z;
4598 for (z = pmap.begin(); z != pmap.end(); ++z) {
4599 if ((*z).tv == &tv) {
4604 assert (z != pmap.end());
4607 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4615 boost::shared_ptr<Region> r = (*x)->region();
4616 boost::shared_ptr<Region> _xx;
4622 pl->remove_region (r);
4623 if (Config->get_edit_mode() == Ripple)
4624 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4628 _xx = RegionFactory::create (r);
4629 npl->add_region (_xx, r->position() - first_position);
4630 pl->remove_region (r);
4631 if (Config->get_edit_mode() == Ripple)
4632 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4636 /* copy region before adding, so we're not putting same object into two different playlists */
4637 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4641 pl->remove_region (r);
4642 if (Config->get_edit_mode() == Ripple)
4643 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4652 list<boost::shared_ptr<Playlist> > foo;
4654 /* the pmap is in the same order as the tracks in which selected regions occurred */
4656 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4659 foo.push_back ((*i).pl);
4664 cut_buffer->set (foo);
4668 _last_cut_copy_source_track = 0;
4670 _last_cut_copy_source_track = pmap.front().tv;
4674 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4677 /* We might have removed regions, which alters other regions' layering_index,
4678 so we need to do a recursive diff here.
4680 vector<Command*> cmds;
4681 (*pl)->rdiff (cmds);
4682 _session->add_commands (cmds);
4684 _session->add_command (new StatefulDiffCommand (*pl));
4689 Editor::cut_copy_ranges (CutCopyOp op)
4691 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4693 /* Sort the track selection now, so that it if is used, the playlists
4694 selected by the calls below to cut_copy_clear are in the order that
4695 their tracks appear in the editor. This makes things like paste
4696 of ranges work properly.
4699 sort_track_selection (ts);
4702 if (!entered_track) {
4705 ts.push_back (entered_track);
4708 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4709 (*i)->cut_copy_clear (*selection, op);
4714 Editor::paste (float times, bool from_context)
4716 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4717 MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
4718 paste_internal (where.frame, times, 0);
4722 Editor::mouse_paste ()
4724 MusicFrame where (0, 0);
4726 if (!mouse_frame (where.frame, ignored)) {
4731 paste_internal (where.frame, 1, where.division);
4735 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4737 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4739 if (cut_buffer->empty(internal_editing())) {
4743 if (position == max_framepos) {
4744 position = get_preferred_edit_position();
4745 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4748 if (position == last_paste_pos) {
4749 /* repeated paste in the same position */
4752 /* paste in new location, reset repeated paste state */
4754 last_paste_pos = position;
4757 /* get everything in the correct order */
4760 if (!selection->tracks.empty()) {
4761 /* If there is a track selection, paste into exactly those tracks and
4762 * only those tracks. This allows the user to be explicit and override
4763 * the below "do the reasonable thing" logic. */
4764 ts = selection->tracks.filter_to_unique_playlists ();
4765 sort_track_selection (ts);
4767 /* Figure out which track to base the paste at. */
4768 TimeAxisView* base_track = NULL;
4769 if (_edit_point == Editing::EditAtMouse && entered_track) {
4770 /* With the mouse edit point, paste onto the track under the mouse. */
4771 base_track = entered_track;
4772 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4773 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4774 base_track = &entered_regionview->get_time_axis_view();
4775 } else if (_last_cut_copy_source_track) {
4776 /* Paste to the track that the cut/copy came from (see mantis #333). */
4777 base_track = _last_cut_copy_source_track;
4779 /* This is "impossible" since we've copied... well, do nothing. */
4783 /* Walk up to parent if necessary, so base track is a route. */
4784 while (base_track->get_parent()) {
4785 base_track = base_track->get_parent();
4788 /* Add base track and all tracks below it. The paste logic will select
4789 the appropriate object types from the cut buffer in relative order. */
4790 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4791 if ((*i)->order() >= base_track->order()) {
4796 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4797 sort_track_selection (ts);
4799 /* Add automation children of each track in order, for pasting several lines. */
4800 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4801 /* Add any automation children for pasting several lines */
4802 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4807 typedef RouteTimeAxisView::AutomationTracks ATracks;
4808 const ATracks& atracks = rtv->automation_tracks();
4809 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4810 i = ts.insert(i, a->second.get());
4815 /* We now have a list of trackviews starting at base_track, including
4816 automation children, in the order shown in the editor, e.g. R1,
4817 R1.A1, R1.A2, R2, R2.A1, ... */
4820 begin_reversible_command (Operations::paste);
4822 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4823 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4824 /* Only one line copied, and one automation track selected. Do a
4825 "greedy" paste from one automation type to another. */
4827 PasteContext ctx(paste_count, times, ItemCounts(), true);
4828 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4832 /* Paste into tracks */
4834 PasteContext ctx(paste_count, times, ItemCounts(), false);
4835 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4836 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4840 commit_reversible_command ();
4844 Editor::duplicate_regions (float times)
4846 RegionSelection rs (get_regions_from_selection_and_entered());
4847 duplicate_some_regions (rs, times);
4851 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4853 if (regions.empty ()) {
4857 boost::shared_ptr<Playlist> playlist;
4858 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4859 RegionSelection foo;
4861 framepos_t const start_frame = regions.start ();
4862 framepos_t const end_frame = regions.end_frame ();
4863 framecnt_t const gap = end_frame - start_frame + 1;
4865 begin_reversible_command (Operations::duplicate_region);
4867 selection->clear_regions ();
4869 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4871 boost::shared_ptr<Region> r ((*i)->region());
4873 TimeAxisView& tv = (*i)->get_time_axis_view();
4874 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4875 latest_regionviews.clear ();
4876 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4878 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4879 playlist = (*i)->region()->playlist();
4880 playlist->clear_changes ();
4881 playlist->duplicate (r, position, gap, times);
4882 _session->add_command(new StatefulDiffCommand (playlist));
4886 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4890 selection->set (foo);
4893 commit_reversible_command ();
4897 Editor::duplicate_selection (float times)
4899 if (selection->time.empty() || selection->tracks.empty()) {
4903 boost::shared_ptr<Playlist> playlist;
4905 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4907 bool in_command = false;
4909 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4910 if ((playlist = (*i)->playlist()) == 0) {
4913 playlist->clear_changes ();
4915 if (clicked_selection) {
4916 playlist->duplicate_range (selection->time[clicked_selection], times);
4918 playlist->duplicate_ranges (selection->time, times);
4922 begin_reversible_command (_("duplicate range selection"));
4925 _session->add_command (new StatefulDiffCommand (playlist));
4930 if (times == 1.0f) {
4931 // now "move" range selection to after the current range selection
4932 framecnt_t distance = 0;
4934 if (clicked_selection) {
4936 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4938 distance = selection->time.end_frame () - selection->time.start ();
4941 selection->move_time (distance);
4943 commit_reversible_command ();
4947 /** Reset all selected points to the relevant default value */
4949 Editor::reset_point_selection ()
4951 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4952 ARDOUR::AutomationList::iterator j = (*i)->model ();
4953 (*j)->value = (*i)->line().the_list()->descriptor ().normal;
4958 Editor::center_playhead ()
4960 float const page = _visible_canvas_width * samples_per_pixel;
4961 center_screen_internal (playhead_cursor->current_frame (), page);
4965 Editor::center_edit_point ()
4967 float const page = _visible_canvas_width * samples_per_pixel;
4968 center_screen_internal (get_preferred_edit_position(), page);
4971 /** Caller must begin and commit a reversible command */
4973 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4975 playlist->clear_changes ();
4977 _session->add_command (new StatefulDiffCommand (playlist));
4981 Editor::nudge_track (bool use_edit, bool forwards)
4983 boost::shared_ptr<Playlist> playlist;
4984 framepos_t distance;
4985 framepos_t next_distance;
4989 start = get_preferred_edit_position();
4994 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4998 if (selection->tracks.empty()) {
5002 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5003 bool in_command = false;
5005 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5007 if ((playlist = (*i)->playlist()) == 0) {
5011 playlist->clear_changes ();
5012 playlist->clear_owned_changes ();
5014 playlist->nudge_after (start, distance, forwards);
5017 begin_reversible_command (_("nudge track"));
5020 vector<Command*> cmds;
5022 playlist->rdiff (cmds);
5023 _session->add_commands (cmds);
5025 _session->add_command (new StatefulDiffCommand (playlist));
5029 commit_reversible_command ();
5034 Editor::remove_last_capture ()
5036 vector<string> choices;
5043 if (Config->get_verify_remove_last_capture()) {
5044 prompt = _("Do you really want to destroy the last capture?"
5045 "\n(This is destructive and cannot be undone)");
5047 choices.push_back (_("No, do nothing."));
5048 choices.push_back (_("Yes, destroy it."));
5050 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5052 if (prompter.run () == 1) {
5053 _session->remove_last_capture ();
5054 _regions->redisplay ();
5058 _session->remove_last_capture();
5059 _regions->redisplay ();
5064 Editor::normalize_region ()
5070 RegionSelection rs = get_regions_from_selection_and_entered ();
5076 NormalizeDialog dialog (rs.size() > 1);
5078 if (dialog.run () != RESPONSE_ACCEPT) {
5082 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5085 /* XXX: should really only count audio regions here */
5086 int const regions = rs.size ();
5088 /* Make a list of the selected audio regions' maximum amplitudes, and also
5089 obtain the maximum amplitude of them all.
5091 list<double> max_amps;
5092 list<double> rms_vals;
5095 bool use_rms = dialog.constrain_rms ();
5097 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5098 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5102 dialog.descend (1.0 / regions);
5103 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5105 double r = arv->audio_region()->rms (&dialog);
5106 max_rms = max (max_rms, r);
5107 rms_vals.push_back (r);
5111 /* the user cancelled the operation */
5115 max_amps.push_back (a);
5116 max_amp = max (max_amp, a);
5120 list<double>::const_iterator a = max_amps.begin ();
5121 list<double>::const_iterator l = rms_vals.begin ();
5122 bool in_command = false;
5124 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5125 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5130 arv->region()->clear_changes ();
5132 double amp = dialog.normalize_individually() ? *a : max_amp;
5133 double target = dialog.target_peak (); // dB
5136 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5137 const double t_rms = dialog.target_rms ();
5138 const gain_t c_peak = dB_to_coefficient (target);
5139 const gain_t c_rms = dB_to_coefficient (t_rms);
5140 if ((amp_rms / c_rms) > (amp / c_peak)) {
5146 arv->audio_region()->normalize (amp, target);
5149 begin_reversible_command (_("normalize"));
5152 _session->add_command (new StatefulDiffCommand (arv->region()));
5159 commit_reversible_command ();
5165 Editor::reset_region_scale_amplitude ()
5171 RegionSelection rs = get_regions_from_selection_and_entered ();
5177 bool in_command = false;
5179 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5180 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5183 arv->region()->clear_changes ();
5184 arv->audio_region()->set_scale_amplitude (1.0f);
5187 begin_reversible_command ("reset gain");
5190 _session->add_command (new StatefulDiffCommand (arv->region()));
5194 commit_reversible_command ();
5199 Editor::adjust_region_gain (bool up)
5201 RegionSelection rs = get_regions_from_selection_and_entered ();
5203 if (!_session || rs.empty()) {
5207 bool in_command = false;
5209 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5210 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5215 arv->region()->clear_changes ();
5217 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5225 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5228 begin_reversible_command ("adjust region gain");
5231 _session->add_command (new StatefulDiffCommand (arv->region()));
5235 commit_reversible_command ();
5240 Editor::reset_region_gain ()
5242 RegionSelection rs = get_regions_from_selection_and_entered ();
5244 if (!_session || rs.empty()) {
5248 bool in_command = false;
5250 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5251 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5256 arv->region()->clear_changes ();
5258 arv->audio_region()->set_scale_amplitude (1.0f);
5261 begin_reversible_command ("reset region gain");
5264 _session->add_command (new StatefulDiffCommand (arv->region()));
5268 commit_reversible_command ();
5273 Editor::reverse_region ()
5279 Reverse rev (*_session);
5280 apply_filter (rev, _("reverse regions"));
5284 Editor::strip_region_silence ()
5290 RegionSelection rs = get_regions_from_selection_and_entered ();
5296 std::list<RegionView*> audio_only;
5298 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5299 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5301 audio_only.push_back (arv);
5305 assert (!audio_only.empty());
5307 StripSilenceDialog d (_session, audio_only);
5308 int const r = d.run ();
5312 if (r == Gtk::RESPONSE_OK) {
5313 ARDOUR::AudioIntervalMap silences;
5314 d.silences (silences);
5315 StripSilence s (*_session, silences, d.fade_length());
5317 apply_filter (s, _("strip silence"), &d);
5322 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5324 Evoral::Sequence<Evoral::Beats>::Notes selected;
5325 mrv.selection_as_notelist (selected, true);
5327 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5328 v.push_back (selected);
5330 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5332 return op (mrv.midi_region()->model(), pos_beats, v);
5336 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5342 bool in_command = false;
5344 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5345 RegionSelection::const_iterator tmp = r;
5348 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5351 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5354 begin_reversible_command (op.name ());
5358 _session->add_command (cmd);
5366 commit_reversible_command ();
5371 Editor::fork_region ()
5373 RegionSelection rs = get_regions_from_selection_and_entered ();
5379 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5380 bool in_command = false;
5384 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5385 RegionSelection::iterator tmp = r;
5388 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5392 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5393 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5394 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5397 begin_reversible_command (_("Fork Region(s)"));
5400 playlist->clear_changes ();
5401 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5402 _session->add_command(new StatefulDiffCommand (playlist));
5404 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5412 commit_reversible_command ();
5417 Editor::quantize_region ()
5420 quantize_regions(get_regions_from_selection_and_entered ());
5425 Editor::quantize_regions (const RegionSelection& rs)
5427 if (rs.n_midi_regions() == 0) {
5431 if (!quantize_dialog) {
5432 quantize_dialog = new QuantizeDialog (*this);
5435 if (quantize_dialog->is_mapped()) {
5436 /* in progress already */
5440 quantize_dialog->present ();
5441 const int r = quantize_dialog->run ();
5442 quantize_dialog->hide ();
5444 if (r == Gtk::RESPONSE_OK) {
5445 Quantize quant (quantize_dialog->snap_start(),
5446 quantize_dialog->snap_end(),
5447 quantize_dialog->start_grid_size(),
5448 quantize_dialog->end_grid_size(),
5449 quantize_dialog->strength(),
5450 quantize_dialog->swing(),
5451 quantize_dialog->threshold());
5453 apply_midi_note_edit_op (quant, rs);
5458 Editor::legatize_region (bool shrink_only)
5461 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5466 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5468 if (rs.n_midi_regions() == 0) {
5472 Legatize legatize(shrink_only);
5473 apply_midi_note_edit_op (legatize, rs);
5477 Editor::transform_region ()
5480 transform_regions(get_regions_from_selection_and_entered ());
5485 Editor::transform_regions (const RegionSelection& rs)
5487 if (rs.n_midi_regions() == 0) {
5494 const int r = td.run();
5497 if (r == Gtk::RESPONSE_OK) {
5498 Transform transform(td.get());
5499 apply_midi_note_edit_op(transform, rs);
5504 Editor::transpose_region ()
5507 transpose_regions(get_regions_from_selection_and_entered ());
5512 Editor::transpose_regions (const RegionSelection& rs)
5514 if (rs.n_midi_regions() == 0) {
5519 int const r = d.run ();
5521 if (r == RESPONSE_ACCEPT) {
5522 Transpose transpose(d.semitones ());
5523 apply_midi_note_edit_op (transpose, rs);
5528 Editor::insert_patch_change (bool from_context)
5530 RegionSelection rs = get_regions_from_selection_and_entered ();
5536 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5538 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5539 there may be more than one, but the PatchChangeDialog can only offer
5540 one set of patch menus.
5542 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5544 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5545 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5547 if (d.run() == RESPONSE_CANCEL) {
5551 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5552 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5554 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5555 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5562 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5564 RegionSelection rs = get_regions_from_selection_and_entered ();
5570 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5571 bool in_command = false;
5576 int const N = rs.size ();
5578 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5579 RegionSelection::iterator tmp = r;
5582 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5584 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5587 progress->descend (1.0 / N);
5590 if (arv->audio_region()->apply (filter, progress) == 0) {
5592 playlist->clear_changes ();
5593 playlist->clear_owned_changes ();
5596 begin_reversible_command (command);
5600 if (filter.results.empty ()) {
5602 /* no regions returned; remove the old one */
5603 playlist->remove_region (arv->region ());
5607 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5609 /* first region replaces the old one */
5610 playlist->replace_region (arv->region(), *res, (*res)->position());
5614 while (res != filter.results.end()) {
5615 playlist->add_region (*res, (*res)->position());
5621 /* We might have removed regions, which alters other regions' layering_index,
5622 so we need to do a recursive diff here.
5624 vector<Command*> cmds;
5625 playlist->rdiff (cmds);
5626 _session->add_commands (cmds);
5628 _session->add_command(new StatefulDiffCommand (playlist));
5632 progress->ascend ();
5641 commit_reversible_command ();
5646 Editor::external_edit_region ()
5652 Editor::reset_region_gain_envelopes ()
5654 RegionSelection rs = get_regions_from_selection_and_entered ();
5656 if (!_session || rs.empty()) {
5660 bool in_command = false;
5662 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5663 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5665 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5666 XMLNode& before (alist->get_state());
5668 arv->audio_region()->set_default_envelope ();
5671 begin_reversible_command (_("reset region gain"));
5674 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5679 commit_reversible_command ();
5684 Editor::set_region_gain_visibility (RegionView* rv)
5686 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5688 arv->update_envelope_visibility();
5693 Editor::set_gain_envelope_visibility ()
5699 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5700 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5702 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5708 Editor::toggle_gain_envelope_active ()
5710 if (_ignore_region_action) {
5714 RegionSelection rs = get_regions_from_selection_and_entered ();
5716 if (!_session || rs.empty()) {
5720 bool in_command = false;
5722 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5723 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5725 arv->region()->clear_changes ();
5726 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5729 begin_reversible_command (_("region gain envelope active"));
5732 _session->add_command (new StatefulDiffCommand (arv->region()));
5737 commit_reversible_command ();
5742 Editor::toggle_region_lock ()
5744 if (_ignore_region_action) {
5748 RegionSelection rs = get_regions_from_selection_and_entered ();
5750 if (!_session || rs.empty()) {
5754 begin_reversible_command (_("toggle region lock"));
5756 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5757 (*i)->region()->clear_changes ();
5758 (*i)->region()->set_locked (!(*i)->region()->locked());
5759 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5762 commit_reversible_command ();
5766 Editor::toggle_region_video_lock ()
5768 if (_ignore_region_action) {
5772 RegionSelection rs = get_regions_from_selection_and_entered ();
5774 if (!_session || rs.empty()) {
5778 begin_reversible_command (_("Toggle Video Lock"));
5780 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5781 (*i)->region()->clear_changes ();
5782 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5783 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5786 commit_reversible_command ();
5790 Editor::toggle_region_lock_style ()
5792 if (_ignore_region_action) {
5796 RegionSelection rs = get_regions_from_selection_and_entered ();
5798 if (!_session || rs.empty()) {
5802 Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
5803 vector<Widget*> proxies = a->get_proxies();
5804 Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
5808 begin_reversible_command (_("toggle region lock style"));
5810 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5811 (*i)->region()->clear_changes ();
5812 PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
5813 (*i)->region()->set_position_lock_style (ns);
5814 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5817 commit_reversible_command ();
5821 Editor::toggle_opaque_region ()
5823 if (_ignore_region_action) {
5827 RegionSelection rs = get_regions_from_selection_and_entered ();
5829 if (!_session || rs.empty()) {
5833 begin_reversible_command (_("change region opacity"));
5835 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5836 (*i)->region()->clear_changes ();
5837 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5838 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5841 commit_reversible_command ();
5845 Editor::toggle_record_enable ()
5847 bool new_state = false;
5849 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5850 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5853 if (!rtav->is_track())
5857 new_state = !rtav->track()->rec_enable_control()->get_value();
5861 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5866 Editor::toggle_solo ()
5868 bool new_state = false;
5870 boost::shared_ptr<ControlList> cl (new ControlList);
5872 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5873 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5880 new_state = !rtav->route()->soloed ();
5884 cl->push_back (rtav->route()->solo_control());
5887 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5891 Editor::toggle_mute ()
5893 bool new_state = false;
5895 boost::shared_ptr<RouteList> rl (new RouteList);
5897 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5898 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5905 new_state = !rtav->route()->muted();
5909 rl->push_back (rtav->route());
5912 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5916 Editor::toggle_solo_isolate ()
5922 Editor::fade_range ()
5924 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5926 begin_reversible_command (_("fade range"));
5928 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5929 (*i)->fade_range (selection->time);
5932 commit_reversible_command ();
5937 Editor::set_fade_length (bool in)
5939 RegionSelection rs = get_regions_from_selection_and_entered ();
5945 /* we need a region to measure the offset from the start */
5947 RegionView* rv = rs.front ();
5949 framepos_t pos = get_preferred_edit_position();
5953 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5954 /* edit point is outside the relevant region */
5959 if (pos <= rv->region()->position()) {
5963 len = pos - rv->region()->position();
5964 cmd = _("set fade in length");
5966 if (pos >= rv->region()->last_frame()) {
5970 len = rv->region()->last_frame() - pos;
5971 cmd = _("set fade out length");
5974 bool in_command = false;
5976 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5977 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5983 boost::shared_ptr<AutomationList> alist;
5985 alist = tmp->audio_region()->fade_in();
5987 alist = tmp->audio_region()->fade_out();
5990 XMLNode &before = alist->get_state();
5993 tmp->audio_region()->set_fade_in_length (len);
5994 tmp->audio_region()->set_fade_in_active (true);
5996 tmp->audio_region()->set_fade_out_length (len);
5997 tmp->audio_region()->set_fade_out_active (true);
6001 begin_reversible_command (cmd);
6004 XMLNode &after = alist->get_state();
6005 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
6009 commit_reversible_command ();
6014 Editor::set_fade_in_shape (FadeShape shape)
6016 RegionSelection rs = get_regions_from_selection_and_entered ();
6021 bool in_command = false;
6023 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6024 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6030 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6031 XMLNode &before = alist->get_state();
6033 tmp->audio_region()->set_fade_in_shape (shape);
6036 begin_reversible_command (_("set fade in shape"));
6039 XMLNode &after = alist->get_state();
6040 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6044 commit_reversible_command ();
6049 Editor::set_fade_out_shape (FadeShape shape)
6051 RegionSelection rs = get_regions_from_selection_and_entered ();
6056 bool in_command = false;
6058 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6059 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6065 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6066 XMLNode &before = alist->get_state();
6068 tmp->audio_region()->set_fade_out_shape (shape);
6071 begin_reversible_command (_("set fade out shape"));
6074 XMLNode &after = alist->get_state();
6075 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6079 commit_reversible_command ();
6084 Editor::set_fade_in_active (bool yn)
6086 RegionSelection rs = get_regions_from_selection_and_entered ();
6091 bool in_command = false;
6093 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6094 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6101 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6103 ar->clear_changes ();
6104 ar->set_fade_in_active (yn);
6107 begin_reversible_command (_("set fade in active"));
6110 _session->add_command (new StatefulDiffCommand (ar));
6114 commit_reversible_command ();
6119 Editor::set_fade_out_active (bool yn)
6121 RegionSelection rs = get_regions_from_selection_and_entered ();
6126 bool in_command = false;
6128 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6129 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6135 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6137 ar->clear_changes ();
6138 ar->set_fade_out_active (yn);
6141 begin_reversible_command (_("set fade out active"));
6144 _session->add_command(new StatefulDiffCommand (ar));
6148 commit_reversible_command ();
6153 Editor::toggle_region_fades (int dir)
6155 if (_ignore_region_action) {
6159 boost::shared_ptr<AudioRegion> ar;
6162 RegionSelection rs = get_regions_from_selection_and_entered ();
6168 RegionSelection::iterator i;
6169 for (i = rs.begin(); i != rs.end(); ++i) {
6170 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6172 yn = ar->fade_out_active ();
6174 yn = ar->fade_in_active ();
6180 if (i == rs.end()) {
6184 /* XXX should this undo-able? */
6185 bool in_command = false;
6187 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6188 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6191 ar->clear_changes ();
6193 if (dir == 1 || dir == 0) {
6194 ar->set_fade_in_active (!yn);
6197 if (dir == -1 || dir == 0) {
6198 ar->set_fade_out_active (!yn);
6201 begin_reversible_command (_("toggle fade active"));
6204 _session->add_command(new StatefulDiffCommand (ar));
6208 commit_reversible_command ();
6213 /** Update region fade visibility after its configuration has been changed */
6215 Editor::update_region_fade_visibility ()
6217 bool _fade_visibility = _session->config.get_show_region_fades ();
6219 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6220 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6222 if (_fade_visibility) {
6223 v->audio_view()->show_all_fades ();
6225 v->audio_view()->hide_all_fades ();
6232 Editor::set_edit_point ()
6235 MusicFrame where (0, 0);
6237 if (!mouse_frame (where.frame, ignored)) {
6243 if (selection->markers.empty()) {
6245 mouse_add_new_marker (where.frame);
6250 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6253 loc->move_to (where.frame, where.division);
6259 Editor::set_playhead_cursor ()
6261 if (entered_marker) {
6262 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6264 MusicFrame where (0, 0);
6267 if (!mouse_frame (where.frame, ignored)) {
6274 _session->request_locate (where.frame, _session->transport_rolling());
6278 //not sure what this was for; remove it for now.
6279 // if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6280 // cancel_time_selection();
6286 Editor::split_region ()
6288 if (_drags->active ()) {
6292 //if a range is selected, separate it
6293 if ( !selection->time.empty()) {
6294 separate_regions_between (selection->time);
6298 //if no range was selected, try to find some regions to split
6299 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6301 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6302 const framepos_t pos = get_preferred_edit_position();
6303 const int32_t division = get_grid_music_divisions (0);
6304 MusicFrame where (pos, division);
6310 split_regions_at (where, rs);
6316 Editor::select_next_route()
6318 if (selection->tracks.empty()) {
6319 selection->set (track_views.front());
6323 TimeAxisView* current = selection->tracks.front();
6327 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6329 if (*i == current) {
6331 if (i != track_views.end()) {
6334 current = (*(track_views.begin()));
6335 //selection->set (*(track_views.begin()));
6341 rui = dynamic_cast<RouteUI *>(current);
6343 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6345 selection->set (current);
6347 ensure_time_axis_view_is_visible (*current, false);
6351 Editor::select_prev_route()
6353 if (selection->tracks.empty()) {
6354 selection->set (track_views.front());
6358 TimeAxisView* current = selection->tracks.front();
6362 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6364 if (*i == current) {
6366 if (i != track_views.rend()) {
6369 current = *(track_views.rbegin());
6374 rui = dynamic_cast<RouteUI *>(current);
6376 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6378 selection->set (current);
6380 ensure_time_axis_view_is_visible (*current, false);
6384 Editor::set_loop_from_selection (bool play)
6386 if (_session == 0) {
6390 framepos_t start, end;
6391 if (!get_selection_extents ( start, end))
6394 set_loop_range (start, end, _("set loop range from selection"));
6397 _session->request_play_loop (true, true);
6402 Editor::set_loop_from_region (bool play)
6404 framepos_t start, end;
6405 if (!get_selection_extents ( start, end))
6408 set_loop_range (start, end, _("set loop range from region"));
6411 _session->request_locate (start, true);
6412 _session->request_play_loop (true);
6417 Editor::set_punch_from_selection ()
6419 if (_session == 0) {
6423 framepos_t start, end;
6424 if (!get_selection_extents ( start, end))
6427 set_punch_range (start, end, _("set punch range from selection"));
6431 Editor::set_auto_punch_range ()
6433 // auto punch in/out button from a single button
6434 // If Punch In is unset, set punch range from playhead to end, enable punch in
6435 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6436 // rewound beyond the Punch In marker, in which case that marker will be moved back
6437 // to the current playhead position.
6438 // If punch out is set, it clears the punch range and Punch In/Out buttons
6440 if (_session == 0) {
6444 Location* tpl = transport_punch_location();
6445 framepos_t now = playhead_cursor->current_frame();
6446 framepos_t begin = now;
6447 framepos_t end = _session->current_end_frame();
6449 if (!_session->config.get_punch_in()) {
6450 // First Press - set punch in and create range from here to eternity
6451 set_punch_range (begin, end, _("Auto Punch In"));
6452 _session->config.set_punch_in(true);
6453 } else if (tpl && !_session->config.get_punch_out()) {
6454 // Second press - update end range marker and set punch_out
6455 if (now < tpl->start()) {
6456 // playhead has been rewound - move start back and pretend nothing happened
6458 set_punch_range (begin, end, _("Auto Punch In/Out"));
6460 // normal case for 2nd press - set the punch out
6461 end = playhead_cursor->current_frame ();
6462 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6463 _session->config.set_punch_out(true);
6466 if (_session->config.get_punch_out()) {
6467 _session->config.set_punch_out(false);
6470 if (_session->config.get_punch_in()) {
6471 _session->config.set_punch_in(false);
6476 // third press - unset punch in/out and remove range
6477 _session->locations()->remove(tpl);
6484 Editor::set_session_extents_from_selection ()
6486 if (_session == 0) {
6490 framepos_t start, end;
6491 if (!get_selection_extents ( start, end))
6495 if ((loc = _session->locations()->session_range_location()) == 0) {
6496 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6498 XMLNode &before = loc->get_state();
6500 _session->set_session_extents (start, end);
6502 XMLNode &after = loc->get_state();
6504 begin_reversible_command (_("set session start/end from selection"));
6506 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6508 commit_reversible_command ();
6511 _session->set_end_is_free (false);
6515 Editor::set_punch_start_from_edit_point ()
6519 MusicFrame start (0, 0);
6520 framepos_t end = max_framepos;
6522 //use the existing punch end, if any
6523 Location* tpl = transport_punch_location();
6528 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6529 start.frame = _session->audible_frame();
6531 start.frame = get_preferred_edit_position();
6534 //snap the selection start/end
6537 //if there's not already a sensible selection endpoint, go "forever"
6538 if (start.frame > end ) {
6542 set_punch_range (start.frame, end, _("set punch start from EP"));
6548 Editor::set_punch_end_from_edit_point ()
6552 framepos_t start = 0;
6553 MusicFrame end (max_framepos, 0);
6555 //use the existing punch start, if any
6556 Location* tpl = transport_punch_location();
6558 start = tpl->start();
6561 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6562 end.frame = _session->audible_frame();
6564 end.frame = get_preferred_edit_position();
6567 //snap the selection start/end
6570 set_punch_range (start, end.frame, _("set punch end from EP"));
6576 Editor::set_loop_start_from_edit_point ()
6580 MusicFrame start (0, 0);
6581 framepos_t end = max_framepos;
6583 //use the existing loop end, if any
6584 Location* tpl = transport_loop_location();
6589 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6590 start.frame = _session->audible_frame();
6592 start.frame = get_preferred_edit_position();
6595 //snap the selection start/end
6598 //if there's not already a sensible selection endpoint, go "forever"
6599 if (start.frame > end ) {
6603 set_loop_range (start.frame, end, _("set loop start from EP"));
6609 Editor::set_loop_end_from_edit_point ()
6613 framepos_t start = 0;
6614 MusicFrame end (max_framepos, 0);
6616 //use the existing loop start, if any
6617 Location* tpl = transport_loop_location();
6619 start = tpl->start();
6622 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6623 end.frame = _session->audible_frame();
6625 end.frame = get_preferred_edit_position();
6628 //snap the selection start/end
6631 set_loop_range (start, end.frame, _("set loop end from EP"));
6636 Editor::set_punch_from_region ()
6638 framepos_t start, end;
6639 if (!get_selection_extents ( start, end))
6642 set_punch_range (start, end, _("set punch range from region"));
6646 Editor::pitch_shift_region ()
6648 RegionSelection rs = get_regions_from_selection_and_entered ();
6650 RegionSelection audio_rs;
6651 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6652 if (dynamic_cast<AudioRegionView*> (*i)) {
6653 audio_rs.push_back (*i);
6657 if (audio_rs.empty()) {
6661 pitch_shift (audio_rs, 1.2);
6665 Editor::set_tempo_from_region ()
6667 RegionSelection rs = get_regions_from_selection_and_entered ();
6669 if (!_session || rs.empty()) {
6673 RegionView* rv = rs.front();
6675 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6679 Editor::use_range_as_bar ()
6681 framepos_t start, end;
6682 if (get_edit_op_range (start, end)) {
6683 define_one_bar (start, end);
6688 Editor::define_one_bar (framepos_t start, framepos_t end)
6690 framepos_t length = end - start;
6692 const Meter& m (_session->tempo_map().meter_at_frame (start));
6694 /* length = 1 bar */
6696 /* We're going to deliver a constant tempo here,
6697 so we can use frames per beat to determine length.
6698 now we want frames per beat.
6699 we have frames per bar, and beats per bar, so ...
6702 /* XXXX METER MATH */
6704 double frames_per_beat = length / m.divisions_per_bar();
6706 /* beats per minute = */
6708 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6710 /* now decide whether to:
6712 (a) set global tempo
6713 (b) add a new tempo marker
6717 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6719 bool do_global = false;
6721 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6723 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6724 at the start, or create a new marker
6727 vector<string> options;
6728 options.push_back (_("Cancel"));
6729 options.push_back (_("Add new marker"));
6730 options.push_back (_("Set global tempo"));
6733 _("Define one bar"),
6734 _("Do you want to set the global tempo or add a new tempo marker?"),
6738 c.set_default_response (2);
6754 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6755 if the marker is at the region starter, change it, otherwise add
6760 begin_reversible_command (_("set tempo from region"));
6761 XMLNode& before (_session->tempo_map().get_state());
6764 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6765 } else if (t.frame() == start) {
6766 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
6768 /* constant tempo */
6769 const Tempo tempo (beats_per_minute, t.note_type());
6770 _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
6773 XMLNode& after (_session->tempo_map().get_state());
6775 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6776 commit_reversible_command ();
6780 Editor::split_region_at_transients ()
6782 AnalysisFeatureList positions;
6784 RegionSelection rs = get_regions_from_selection_and_entered ();
6786 if (!_session || rs.empty()) {
6790 begin_reversible_command (_("split regions"));
6792 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6794 RegionSelection::iterator tmp;
6799 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6802 ar->transients (positions);
6803 split_region_at_points ((*i)->region(), positions, true);
6810 commit_reversible_command ();
6815 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6817 bool use_rhythmic_rodent = false;
6819 boost::shared_ptr<Playlist> pl = r->playlist();
6821 list<boost::shared_ptr<Region> > new_regions;
6827 if (positions.empty()) {
6831 if (positions.size() > 20 && can_ferret) {
6832 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);
6833 MessageDialog msg (msgstr,
6836 Gtk::BUTTONS_OK_CANCEL);
6839 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6840 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6842 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6845 msg.set_title (_("Excessive split?"));
6848 int response = msg.run();
6854 case RESPONSE_APPLY:
6855 use_rhythmic_rodent = true;
6862 if (use_rhythmic_rodent) {
6863 show_rhythm_ferret ();
6867 AnalysisFeatureList::const_iterator x;
6869 pl->clear_changes ();
6870 pl->clear_owned_changes ();
6872 x = positions.begin();
6874 if (x == positions.end()) {
6879 pl->remove_region (r);
6883 framepos_t rstart = r->first_frame ();
6884 framepos_t rend = r->last_frame ();
6886 while (x != positions.end()) {
6888 /* deal with positons that are out of scope of present region bounds */
6889 if (*x <= rstart || *x > rend) {
6894 /* file start = original start + how far we from the initial position ? */
6896 framepos_t file_start = r->start() + pos;
6898 /* length = next position - current position */
6900 framepos_t len = (*x) - pos - rstart;
6902 /* XXX we do we really want to allow even single-sample regions?
6903 * shouldn't we have some kind of lower limit on region size?
6912 if (RegionFactory::region_name (new_name, r->name())) {
6916 /* do NOT announce new regions 1 by one, just wait till they are all done */
6920 plist.add (ARDOUR::Properties::start, file_start);
6921 plist.add (ARDOUR::Properties::length, len);
6922 plist.add (ARDOUR::Properties::name, new_name);
6923 plist.add (ARDOUR::Properties::layer, 0);
6924 // TODO set transients_offset
6926 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6927 /* because we set annouce to false, manually add the new region to the
6930 RegionFactory::map_add (nr);
6932 pl->add_region (nr, rstart + pos);
6935 new_regions.push_front(nr);
6944 RegionFactory::region_name (new_name, r->name());
6946 /* Add the final region */
6949 plist.add (ARDOUR::Properties::start, r->start() + pos);
6950 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6951 plist.add (ARDOUR::Properties::name, new_name);
6952 plist.add (ARDOUR::Properties::layer, 0);
6954 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6955 /* because we set annouce to false, manually add the new region to the
6958 RegionFactory::map_add (nr);
6959 pl->add_region (nr, r->position() + pos);
6962 new_regions.push_front(nr);
6967 /* We might have removed regions, which alters other regions' layering_index,
6968 so we need to do a recursive diff here.
6970 vector<Command*> cmds;
6972 _session->add_commands (cmds);
6974 _session->add_command (new StatefulDiffCommand (pl));
6978 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6979 set_selected_regionview_from_region_list ((*i), Selection::Add);
6985 Editor::place_transient()
6991 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6997 framepos_t where = get_preferred_edit_position();
6999 begin_reversible_command (_("place transient"));
7001 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7002 (*r)->region()->add_transient(where);
7005 commit_reversible_command ();
7009 Editor::remove_transient(ArdourCanvas::Item* item)
7015 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
7018 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
7019 _arv->remove_transient (*(float*) _line->get_data ("position"));
7023 Editor::snap_regions_to_grid ()
7025 list <boost::shared_ptr<Playlist > > used_playlists;
7027 RegionSelection rs = get_regions_from_selection_and_entered ();
7029 if (!_session || rs.empty()) {
7033 begin_reversible_command (_("snap regions to grid"));
7035 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7037 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7039 if (!pl->frozen()) {
7040 /* we haven't seen this playlist before */
7042 /* remember used playlists so we can thaw them later */
7043 used_playlists.push_back(pl);
7046 (*r)->region()->clear_changes ();
7048 MusicFrame start ((*r)->region()->first_frame (), 0);
7050 (*r)->region()->set_position (start.frame, start.division);
7051 _session->add_command(new StatefulDiffCommand ((*r)->region()));
7054 while (used_playlists.size() > 0) {
7055 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7057 used_playlists.pop_front();
7060 commit_reversible_command ();
7064 Editor::close_region_gaps ()
7066 list <boost::shared_ptr<Playlist > > used_playlists;
7068 RegionSelection rs = get_regions_from_selection_and_entered ();
7070 if (!_session || rs.empty()) {
7074 Dialog dialog (_("Close Region Gaps"));
7077 table.set_spacings (12);
7078 table.set_border_width (12);
7079 Label* l = manage (left_aligned_label (_("Crossfade length")));
7080 table.attach (*l, 0, 1, 0, 1);
7082 SpinButton spin_crossfade (1, 0);
7083 spin_crossfade.set_range (0, 15);
7084 spin_crossfade.set_increments (1, 1);
7085 spin_crossfade.set_value (5);
7086 table.attach (spin_crossfade, 1, 2, 0, 1);
7088 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7090 l = manage (left_aligned_label (_("Pull-back length")));
7091 table.attach (*l, 0, 1, 1, 2);
7093 SpinButton spin_pullback (1, 0);
7094 spin_pullback.set_range (0, 100);
7095 spin_pullback.set_increments (1, 1);
7096 spin_pullback.set_value(30);
7097 table.attach (spin_pullback, 1, 2, 1, 2);
7099 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7101 dialog.get_vbox()->pack_start (table);
7102 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7103 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7106 if (dialog.run () == RESPONSE_CANCEL) {
7110 framepos_t crossfade_len = spin_crossfade.get_value();
7111 framepos_t pull_back_frames = spin_pullback.get_value();
7113 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7114 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7116 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7118 begin_reversible_command (_("close region gaps"));
7121 boost::shared_ptr<Region> last_region;
7123 rs.sort_by_position_and_track();
7125 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7127 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7129 if (!pl->frozen()) {
7130 /* we haven't seen this playlist before */
7132 /* remember used playlists so we can thaw them later */
7133 used_playlists.push_back(pl);
7137 framepos_t position = (*r)->region()->position();
7139 if (idx == 0 || position < last_region->position()){
7140 last_region = (*r)->region();
7145 (*r)->region()->clear_changes ();
7146 (*r)->region()->trim_front( (position - pull_back_frames));
7148 last_region->clear_changes ();
7149 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7151 _session->add_command (new StatefulDiffCommand ((*r)->region()));
7152 _session->add_command (new StatefulDiffCommand (last_region));
7154 last_region = (*r)->region();
7158 while (used_playlists.size() > 0) {
7159 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7161 used_playlists.pop_front();
7164 commit_reversible_command ();
7168 Editor::tab_to_transient (bool forward)
7170 AnalysisFeatureList positions;
7172 RegionSelection rs = get_regions_from_selection_and_entered ();
7178 framepos_t pos = _session->audible_frame ();
7180 if (!selection->tracks.empty()) {
7182 /* don't waste time searching for transients in duplicate playlists.
7185 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7187 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7189 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7192 boost::shared_ptr<Track> tr = rtv->track();
7194 boost::shared_ptr<Playlist> pl = tr->playlist ();
7196 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7199 positions.push_back (result);
7212 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7213 (*r)->region()->get_transients (positions);
7217 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7220 AnalysisFeatureList::iterator x;
7222 for (x = positions.begin(); x != positions.end(); ++x) {
7228 if (x != positions.end ()) {
7229 _session->request_locate (*x);
7233 AnalysisFeatureList::reverse_iterator x;
7235 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7241 if (x != positions.rend ()) {
7242 _session->request_locate (*x);
7248 Editor::playhead_forward_to_grid ()
7254 MusicFrame pos (playhead_cursor->current_frame (), 0);
7256 if (pos.frame < max_framepos - 1) {
7258 snap_to_internal (pos, RoundUpAlways, false);
7259 _session->request_locate (pos.frame);
7265 Editor::playhead_backward_to_grid ()
7271 MusicFrame pos (playhead_cursor->current_frame (), 0);
7273 if (pos.frame > 2) {
7275 snap_to_internal (pos, RoundDownAlways, false);
7276 _session->request_locate (pos.frame);
7281 Editor::set_track_height (Height h)
7283 TrackSelection& ts (selection->tracks);
7285 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7286 (*x)->set_height_enum (h);
7291 Editor::toggle_tracks_active ()
7293 TrackSelection& ts (selection->tracks);
7295 bool target = false;
7301 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7302 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7306 target = !rtv->_route->active();
7309 rtv->_route->set_active (target, this);
7315 Editor::remove_tracks ()
7317 /* this will delete GUI objects that may be the subject of an event
7318 handler in which this method is called. Defer actual deletion to the
7319 next idle callback, when all event handling is finished.
7321 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7325 Editor::idle_remove_tracks ()
7327 Session::StateProtector sp (_session);
7329 return false; /* do not call again */
7333 Editor::_remove_tracks ()
7335 TrackSelection& ts (selection->tracks);
7341 vector<string> choices;
7345 const char* trackstr;
7347 vector<boost::shared_ptr<Route> > routes;
7348 bool special_bus = false;
7350 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7351 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7355 if (rtv->is_track()) {
7360 routes.push_back (rtv->_route);
7362 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7367 if (special_bus && !Config->get_allow_special_bus_removal()) {
7368 MessageDialog msg (_("That would be bad news ...."),
7372 msg.set_secondary_text (string_compose (_(
7373 "Removing the master or monitor bus is such a bad idea\n\
7374 that %1 is not going to allow it.\n\
7376 If you really want to do this sort of thing\n\
7377 edit your ardour.rc file to set the\n\
7378 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7385 if (ntracks + nbusses == 0) {
7389 trackstr = P_("track", "tracks", ntracks);
7390 busstr = P_("bus", "busses", nbusses);
7394 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7395 "(You may also lose the playlists associated with the %2)\n\n"
7396 "This action cannot be undone, and the session file will be overwritten!"),
7397 ntracks, trackstr, nbusses, busstr);
7399 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7400 "(You may also lose the playlists associated with the %2)\n\n"
7401 "This action cannot be undone, and the session file will be overwritten!"),
7404 } else if (nbusses) {
7405 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7406 "This action cannot be undone, and the session file will be overwritten"),
7410 choices.push_back (_("No, do nothing."));
7411 if (ntracks + nbusses > 1) {
7412 choices.push_back (_("Yes, remove them."));
7414 choices.push_back (_("Yes, remove it."));
7419 title = string_compose (_("Remove %1"), trackstr);
7421 title = string_compose (_("Remove %1"), busstr);
7424 Choice prompter (title, prompt, choices);
7426 if (prompter.run () != 1) {
7430 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7431 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7432 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7433 * likely because deletion requires selection) this will call
7434 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7435 * It's likewise likely that the route that has just been displayed in the
7436 * Editor-Mixer will be next in line for deletion.
7438 * So simply switch to the master-bus (if present)
7440 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7441 if ((*i)->stripable ()->is_master ()) {
7442 set_selected_mixer_strip (*(*i));
7449 PresentationInfo::ChangeSuspender cs;
7450 DisplaySuspender ds;
7452 boost::shared_ptr<RouteList> rl (new RouteList);
7453 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7456 _session->remove_routes (rl);
7458 /* TrackSelection and RouteList leave scope,
7459 * destructors are called,
7460 * diskstream drops references, save_state is called (again for every track)
7465 Editor::do_insert_time ()
7467 if (selection->tracks.empty()) {
7471 InsertRemoveTimeDialog d (*this);
7472 int response = d.run ();
7474 if (response != RESPONSE_OK) {
7478 if (d.distance() == 0) {
7485 d.intersected_region_action (),
7489 d.move_glued_markers(),
7490 d.move_locked_markers(),
7496 Editor::insert_time (
7497 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7498 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7502 if (Config->get_edit_mode() == Lock) {
7505 bool in_command = false;
7507 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7509 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7513 /* don't operate on any playlist more than once, which could
7514 * happen if "all playlists" is enabled, but there is more
7515 * than 1 track using playlists "from" a given track.
7518 set<boost::shared_ptr<Playlist> > pl;
7520 if (all_playlists) {
7521 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7522 if (rtav && rtav->track ()) {
7523 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7524 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7529 if ((*x)->playlist ()) {
7530 pl.insert ((*x)->playlist ());
7534 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7536 (*i)->clear_changes ();
7537 (*i)->clear_owned_changes ();
7540 begin_reversible_command (_("insert time"));
7544 if (opt == SplitIntersected) {
7545 /* non musical split */
7546 (*i)->split (MusicFrame (pos, 0));
7549 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7551 vector<Command*> cmds;
7553 _session->add_commands (cmds);
7555 _session->add_command (new StatefulDiffCommand (*i));
7559 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7562 begin_reversible_command (_("insert time"));
7565 rtav->route ()->shift (pos, frames);
7572 const int32_t divisions = get_grid_music_divisions (0);
7573 XMLNode& before (_session->locations()->get_state());
7574 Locations::LocationList copy (_session->locations()->list());
7576 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7578 Locations::LocationList::const_iterator tmp;
7580 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7581 bool const was_locked = (*i)->locked ();
7582 if (locked_markers_too) {
7586 if ((*i)->start() >= pos) {
7587 // move end first, in case we're moving by more than the length of the range
7588 if (!(*i)->is_mark()) {
7589 (*i)->set_end ((*i)->end() + frames, false, true, divisions);
7591 (*i)->set_start ((*i)->start() + frames, false, true, divisions);
7603 begin_reversible_command (_("insert time"));
7606 XMLNode& after (_session->locations()->get_state());
7607 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7613 begin_reversible_command (_("insert time"));
7616 XMLNode& before (_session->tempo_map().get_state());
7617 _session->tempo_map().insert_time (pos, frames);
7618 XMLNode& after (_session->tempo_map().get_state());
7619 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7623 commit_reversible_command ();
7628 Editor::do_remove_time ()
7630 if (selection->tracks.empty()) {
7634 InsertRemoveTimeDialog d (*this, true);
7636 int response = d.run ();
7638 if (response != RESPONSE_OK) {
7642 framecnt_t distance = d.distance();
7644 if (distance == 0) {
7654 d.move_glued_markers(),
7655 d.move_locked_markers(),
7661 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7662 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7664 if (Config->get_edit_mode() == Lock) {
7665 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7668 bool in_command = false;
7670 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7672 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7676 XMLNode &before = pl->get_state();
7679 begin_reversible_command (_("remove time"));
7683 std::list<AudioRange> rl;
7684 AudioRange ar(pos, pos+frames, 0);
7687 pl->shift (pos, -frames, true, ignore_music_glue);
7689 XMLNode &after = pl->get_state();
7691 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7695 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7698 begin_reversible_command (_("remove time"));
7701 rtav->route ()->shift (pos, -frames);
7705 const int32_t divisions = get_grid_music_divisions (0);
7706 std::list<Location*> loc_kill_list;
7711 XMLNode& before (_session->locations()->get_state());
7712 Locations::LocationList copy (_session->locations()->list());
7714 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7715 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7717 bool const was_locked = (*i)->locked ();
7718 if (locked_markers_too) {
7722 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7723 if ((*i)->end() >= pos
7724 && (*i)->end() < pos+frames
7725 && (*i)->start() >= pos
7726 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7728 loc_kill_list.push_back(*i);
7729 } else { // only start or end is included, try to do the right thing
7730 // move start before moving end, to avoid trying to move the end to before the start
7731 // if we're removing more time than the length of the range
7732 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7733 // start is within cut
7734 (*i)->set_start (pos, false, true,divisions); // bring the start marker to the beginning of the cut
7736 } else if ((*i)->start() >= pos+frames) {
7737 // start (and thus entire range) lies beyond end of cut
7738 (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
7741 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7742 // end is inside cut
7743 (*i)->set_end (pos, false, true, divisions); // bring the end to the cut
7745 } else if ((*i)->end() >= pos+frames) {
7746 // end is beyond end of cut
7747 (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
7752 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7753 loc_kill_list.push_back(*i);
7755 } else if ((*i)->start() >= pos) {
7756 (*i)->set_start ((*i)->start() -frames, false, true, divisions);
7766 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7767 _session->locations()->remove( *i );
7772 begin_reversible_command (_("remove time"));
7775 XMLNode& after (_session->locations()->get_state());
7776 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7781 XMLNode& before (_session->tempo_map().get_state());
7783 if (_session->tempo_map().remove_time (pos, frames) ) {
7785 begin_reversible_command (_("remove time"));
7788 XMLNode& after (_session->tempo_map().get_state());
7789 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7794 commit_reversible_command ();
7799 Editor::fit_selection ()
7801 if (!selection->tracks.empty()) {
7802 fit_tracks (selection->tracks);
7806 /* no selected tracks - use tracks with selected regions */
7808 if (!selection->regions.empty()) {
7809 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7810 tvl.push_back (&(*r)->get_time_axis_view ());
7816 } else if (internal_editing()) {
7817 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7820 if (entered_track) {
7821 tvl.push_back (entered_track);
7829 Editor::fit_tracks (TrackViewList & tracks)
7831 if (tracks.empty()) {
7835 uint32_t child_heights = 0;
7836 int visible_tracks = 0;
7838 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7840 if (!(*t)->marked_for_display()) {
7844 child_heights += (*t)->effective_height() - (*t)->current_height();
7848 /* compute the per-track height from:
7850 * total canvas visible height
7851 * - height that will be taken by visible children of selected tracks
7852 * - height of the ruler/hscroll area
7854 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7855 double first_y_pos = DBL_MAX;
7857 if (h < TimeAxisView::preset_height (HeightSmall)) {
7858 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7859 /* too small to be displayed */
7863 undo_visual_stack.push_back (current_visual_state (true));
7864 PBD::Unwinder<bool> nsv (no_save_visual, true);
7866 /* build a list of all tracks, including children */
7869 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7871 TimeAxisView::Children c = (*i)->get_child_list ();
7872 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7873 all.push_back (j->get());
7878 // find selection range.
7879 // if someone knows how to user TrackViewList::iterator for this
7881 int selected_top = -1;
7882 int selected_bottom = -1;
7884 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7885 if ((*t)->marked_for_display ()) {
7886 if (tracks.contains(*t)) {
7887 if (selected_top == -1) {
7890 selected_bottom = i;
7896 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7897 if ((*t)->marked_for_display ()) {
7898 if (tracks.contains(*t)) {
7899 (*t)->set_height (h);
7900 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7902 if (i > selected_top && i < selected_bottom) {
7903 hide_track_in_display (*t);
7910 set the controls_layout height now, because waiting for its size
7911 request signal handler will cause the vertical adjustment setting to fail
7914 controls_layout.property_height () = _full_canvas_height;
7915 vertical_adjustment.set_value (first_y_pos);
7917 redo_visual_stack.push_back (current_visual_state (true));
7919 visible_tracks_selector.set_text (_("Sel"));
7923 Editor::save_visual_state (uint32_t n)
7925 while (visual_states.size() <= n) {
7926 visual_states.push_back (0);
7929 if (visual_states[n] != 0) {
7930 delete visual_states[n];
7933 visual_states[n] = current_visual_state (true);
7938 Editor::goto_visual_state (uint32_t n)
7940 if (visual_states.size() <= n) {
7944 if (visual_states[n] == 0) {
7948 use_visual_state (*visual_states[n]);
7952 Editor::start_visual_state_op (uint32_t n)
7954 save_visual_state (n);
7956 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7958 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7959 pup->set_text (buf);
7964 Editor::cancel_visual_state_op (uint32_t n)
7966 goto_visual_state (n);
7970 Editor::toggle_region_mute ()
7972 if (_ignore_region_action) {
7976 RegionSelection rs = get_regions_from_selection_and_entered ();
7982 if (rs.size() > 1) {
7983 begin_reversible_command (_("mute regions"));
7985 begin_reversible_command (_("mute region"));
7988 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7990 (*i)->region()->playlist()->clear_changes ();
7991 (*i)->region()->set_muted (!(*i)->region()->muted ());
7992 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7996 commit_reversible_command ();
8000 Editor::combine_regions ()
8002 /* foreach track with selected regions, take all selected regions
8003 and join them into a new region containing the subregions (as a
8007 typedef set<RouteTimeAxisView*> RTVS;
8010 if (selection->regions.empty()) {
8014 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8015 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8018 tracks.insert (rtv);
8022 begin_reversible_command (_("combine regions"));
8024 vector<RegionView*> new_selection;
8026 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8029 if ((rv = (*i)->combine_regions ()) != 0) {
8030 new_selection.push_back (rv);
8034 selection->clear_regions ();
8035 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
8036 selection->add (*i);
8039 commit_reversible_command ();
8043 Editor::uncombine_regions ()
8045 typedef set<RouteTimeAxisView*> RTVS;
8048 if (selection->regions.empty()) {
8052 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
8053 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
8056 tracks.insert (rtv);
8060 begin_reversible_command (_("uncombine regions"));
8062 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8063 (*i)->uncombine_regions ();
8066 commit_reversible_command ();
8070 Editor::toggle_midi_input_active (bool flip_others)
8073 boost::shared_ptr<RouteList> rl (new RouteList);
8075 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8076 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8082 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8085 rl->push_back (rtav->route());
8086 onoff = !mt->input_active();
8090 _session->set_exclusive_input_active (rl, onoff, flip_others);
8093 static bool ok_fine (GdkEventAny*) { return true; }
8099 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8101 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8102 lock_dialog->get_vbox()->pack_start (*padlock);
8103 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8105 ArdourButton* b = manage (new ArdourButton);
8106 b->set_name ("lock button");
8107 b->set_text (_("Click to unlock"));
8108 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8109 lock_dialog->get_vbox()->pack_start (*b);
8111 lock_dialog->get_vbox()->show_all ();
8112 lock_dialog->set_size_request (200, 200);
8115 delete _main_menu_disabler;
8116 _main_menu_disabler = new MainMenuDisabler;
8118 lock_dialog->present ();
8120 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8126 lock_dialog->hide ();
8128 delete _main_menu_disabler;
8129 _main_menu_disabler = 0;
8131 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8132 start_lock_event_timing ();
8137 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8139 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8143 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8145 Timers::TimerSuspender t;
8146 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8147 Gtkmm2ext::UI::instance()->flush_pending (1);
8151 Editor::bring_all_sources_into_session ()
8158 ArdourDialog w (_("Moving embedded files into session folder"));
8159 w.get_vbox()->pack_start (msg);
8162 /* flush all pending GUI events because we're about to start copying
8166 Timers::TimerSuspender t;
8167 Gtkmm2ext::UI::instance()->flush_pending (3);
8171 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));