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 Editing;
114 using Gtkmm2ext::Keyboard;
116 /***********************************************************************
118 ***********************************************************************/
121 Editor::undo (uint32_t n)
123 if (_session && _session->actively_recording()) {
124 /* no undo allowed while recording. Session will check also,
125 but we don't even want to get to that.
130 if (_drags->active ()) {
136 if (_session->undo_depth() == 0) {
137 undo_action->set_sensitive(false);
139 redo_action->set_sensitive(true);
140 begin_selection_op_history ();
145 Editor::redo (uint32_t n)
147 if (_session && _session->actively_recording()) {
148 /* no redo allowed while recording. Session will check also,
149 but we don't even want to get to that.
154 if (_drags->active ()) {
160 if (_session->redo_depth() == 0) {
161 redo_action->set_sensitive(false);
163 undo_action->set_sensitive(true);
164 begin_selection_op_history ();
169 Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num,
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)) {
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, sub_num);
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 _ignore_follow_edits = true; // a split will change the region selection in mysterious ways; it's not practical or wanted to follow this edit
282 RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
283 /* There are three classes of regions that we might want selected after
284 splitting selected regions:
285 - regions selected before the split operation, and unaffected by it
286 - newly-created regions before the split
287 - newly-created regions after the split
290 if (rsas & Existing) {
291 // region selections that existed before the split.
292 selection->add ( pre_selected_regions );
295 for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
296 if ((*ri)->region()->position() < where) {
297 // new regions created before the split
298 if (rsas & NewlyCreatedLeft) {
299 selection->add (*ri);
302 // new regions created after the split
303 if (rsas & NewlyCreatedRight) {
304 selection->add (*ri);
308 _ignore_follow_edits = false;
310 _ignore_follow_edits = true;
311 if( working_on_selection ) {
312 selection->add (latest_regionviews); //these are the new regions created after the split
314 _ignore_follow_edits = false;
317 commit_reversible_command ();
320 /** Move one extreme of the current range selection. If more than one range is selected,
321 * the start of the earliest range or the end of the latest range is moved.
323 * @param move_end true to move the end of the current range selection, false to move
325 * @param next true to move the extreme to the next region boundary, false to move to
329 Editor::move_range_selection_start_or_end_to_region_boundary (bool move_end, bool next)
331 if (selection->time.start() == selection->time.end_frame()) {
335 framepos_t start = selection->time.start ();
336 framepos_t end = selection->time.end_frame ();
338 /* the position of the thing we may move */
339 framepos_t pos = move_end ? end : start;
340 int dir = next ? 1 : -1;
342 /* so we don't find the current region again */
343 if (dir > 0 || pos > 0) {
347 framepos_t const target = get_region_boundary (pos, dir, true, false);
362 begin_reversible_selection_op (_("alter selection"));
363 selection->set_preserving_all_ranges (start, end);
364 commit_reversible_selection_op ();
368 Editor::nudge_forward_release (GdkEventButton* ev)
370 if (ev->state & Keyboard::PrimaryModifier) {
371 nudge_forward (false, true);
373 nudge_forward (false, false);
379 Editor::nudge_backward_release (GdkEventButton* ev)
381 if (ev->state & Keyboard::PrimaryModifier) {
382 nudge_backward (false, true);
384 nudge_backward (false, false);
391 Editor::nudge_forward (bool next, bool force_playhead)
394 framepos_t next_distance;
400 RegionSelection rs = get_regions_from_selection_and_entered ();
402 if (!force_playhead && !rs.empty()) {
404 begin_reversible_command (_("nudge regions forward"));
406 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
407 boost::shared_ptr<Region> r ((*i)->region());
409 distance = get_nudge_distance (r->position(), next_distance);
412 distance = next_distance;
416 r->set_position (r->position() + distance);
417 _session->add_command (new StatefulDiffCommand (r));
420 commit_reversible_command ();
423 } else if (!force_playhead && !selection->markers.empty()) {
426 bool in_command = false;
428 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
430 Location* loc = find_location_from_marker ((*i), is_start);
434 XMLNode& before (loc->get_state());
437 distance = get_nudge_distance (loc->start(), next_distance);
439 distance = next_distance;
441 if (max_framepos - distance > loc->start() + loc->length()) {
442 loc->set_start (loc->start() + distance);
444 loc->set_start (max_framepos - loc->length());
447 distance = get_nudge_distance (loc->end(), next_distance);
449 distance = next_distance;
451 if (max_framepos - distance > loc->end()) {
452 loc->set_end (loc->end() + distance);
454 loc->set_end (max_framepos);
456 if (loc->is_session_range()) {
457 _session->set_end_is_free (false);
461 begin_reversible_command (_("nudge location forward"));
464 XMLNode& after (loc->get_state());
465 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
470 commit_reversible_command ();
473 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
474 _session->request_locate (playhead_cursor->current_frame () + distance);
479 Editor::nudge_backward (bool next, bool force_playhead)
482 framepos_t next_distance;
488 RegionSelection rs = get_regions_from_selection_and_entered ();
490 if (!force_playhead && !rs.empty()) {
492 begin_reversible_command (_("nudge regions backward"));
494 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
495 boost::shared_ptr<Region> r ((*i)->region());
497 distance = get_nudge_distance (r->position(), next_distance);
500 distance = next_distance;
505 if (r->position() > distance) {
506 r->set_position (r->position() - distance);
510 _session->add_command (new StatefulDiffCommand (r));
513 commit_reversible_command ();
515 } else if (!force_playhead && !selection->markers.empty()) {
518 bool in_command = false;
520 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
522 Location* loc = find_location_from_marker ((*i), is_start);
526 XMLNode& before (loc->get_state());
529 distance = get_nudge_distance (loc->start(), next_distance);
531 distance = next_distance;
533 if (distance < loc->start()) {
534 loc->set_start (loc->start() - distance);
539 distance = get_nudge_distance (loc->end(), next_distance);
542 distance = next_distance;
545 if (distance < loc->end() - loc->length()) {
546 loc->set_end (loc->end() - distance);
548 loc->set_end (loc->length());
550 if (loc->is_session_range()) {
551 _session->set_end_is_free (false);
555 begin_reversible_command (_("nudge location forward"));
558 XMLNode& after (loc->get_state());
559 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
563 commit_reversible_command ();
568 distance = get_nudge_distance (playhead_cursor->current_frame (), next_distance);
570 if (playhead_cursor->current_frame () > distance) {
571 _session->request_locate (playhead_cursor->current_frame () - distance);
573 _session->goto_start();
579 Editor::nudge_forward_capture_offset ()
581 RegionSelection rs = get_regions_from_selection_and_entered ();
583 if (!_session || rs.empty()) {
587 begin_reversible_command (_("nudge forward"));
589 framepos_t const distance = _session->worst_output_latency();
591 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
592 boost::shared_ptr<Region> r ((*i)->region());
595 r->set_position (r->position() + distance);
596 _session->add_command(new StatefulDiffCommand (r));
599 commit_reversible_command ();
603 Editor::nudge_backward_capture_offset ()
605 RegionSelection rs = get_regions_from_selection_and_entered ();
607 if (!_session || rs.empty()) {
611 begin_reversible_command (_("nudge backward"));
613 framepos_t const distance = _session->worst_output_latency();
615 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
616 boost::shared_ptr<Region> r ((*i)->region());
620 if (r->position() > distance) {
621 r->set_position (r->position() - distance);
625 _session->add_command(new StatefulDiffCommand (r));
628 commit_reversible_command ();
631 struct RegionSelectionPositionSorter {
632 bool operator() (RegionView* a, RegionView* b) {
633 return a->region()->position() < b->region()->position();
638 Editor::sequence_regions ()
641 framepos_t r_end_prev;
649 RegionSelection rs = get_regions_from_selection_and_entered ();
650 rs.sort(RegionSelectionPositionSorter());
654 bool in_command = false;
656 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
657 boost::shared_ptr<Region> r ((*i)->region());
665 if(r->position_locked())
672 r->set_position(r_end_prev);
676 begin_reversible_command (_("sequence regions"));
679 _session->add_command (new StatefulDiffCommand (r));
681 r_end=r->position() + r->length();
687 commit_reversible_command ();
696 Editor::move_to_start ()
698 _session->goto_start ();
702 Editor::move_to_end ()
705 _session->request_locate (_session->current_end_frame());
709 Editor::build_region_boundary_cache ()
712 vector<RegionPoint> interesting_points;
713 boost::shared_ptr<Region> r;
714 TrackViewList tracks;
717 region_boundary_cache.clear ();
723 switch (_snap_type) {
724 case SnapToRegionStart:
725 interesting_points.push_back (Start);
727 case SnapToRegionEnd:
728 interesting_points.push_back (End);
730 case SnapToRegionSync:
731 interesting_points.push_back (SyncPoint);
733 case SnapToRegionBoundary:
734 interesting_points.push_back (Start);
735 interesting_points.push_back (End);
738 fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
739 abort(); /*NOTREACHED*/
743 TimeAxisView *ontrack = 0;
746 if (!selection->tracks.empty()) {
747 tlist = selection->tracks.filter_to_unique_playlists ();
749 tlist = track_views.filter_to_unique_playlists ();
752 while (pos < _session->current_end_frame() && !at_end) {
755 framepos_t lpos = max_framepos;
757 for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
759 if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) {
760 if (*p == interesting_points.back()) {
763 /* move to next point type */
769 rpos = r->first_frame();
773 rpos = r->last_frame();
777 rpos = r->sync_position ();
785 RouteTimeAxisView *rtav;
787 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
788 if (rtav->track() != 0) {
789 speed = rtav->track()->speed();
793 rpos = track_frame_to_session_frame (rpos, speed);
799 /* prevent duplicates, but we don't use set<> because we want to be able
803 vector<framepos_t>::iterator ri;
805 for (ri = region_boundary_cache.begin(); ri != region_boundary_cache.end(); ++ri) {
811 if (ri == region_boundary_cache.end()) {
812 region_boundary_cache.push_back (rpos);
819 /* finally sort to be sure that the order is correct */
821 sort (region_boundary_cache.begin(), region_boundary_cache.end());
824 boost::shared_ptr<Region>
825 Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, TrackViewList& tracks, TimeAxisView **ontrack)
827 TrackViewList::iterator i;
828 framepos_t closest = max_framepos;
829 boost::shared_ptr<Region> ret;
833 framepos_t track_frame;
834 RouteTimeAxisView *rtav;
836 for (i = tracks.begin(); i != tracks.end(); ++i) {
839 boost::shared_ptr<Region> r;
842 if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
843 if (rtav->track()!=0)
844 track_speed = rtav->track()->speed();
847 track_frame = session_frame_to_track_frame(frame, track_speed);
849 if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
855 rpos = r->first_frame ();
859 rpos = r->last_frame ();
863 rpos = r->sync_position ();
867 // rpos is a "track frame", converting it to "_session frame"
868 rpos = track_frame_to_session_frame(rpos, track_speed);
871 distance = rpos - frame;
873 distance = frame - rpos;
876 if (distance < closest) {
888 Editor::find_next_region_boundary (framepos_t pos, int32_t dir, const TrackViewList& tracks)
890 framecnt_t distance = max_framepos;
891 framepos_t current_nearest = -1;
893 for (TrackViewList::const_iterator i = tracks.begin(); i != tracks.end(); ++i) {
894 framepos_t contender;
897 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
903 if ((contender = rtv->find_next_region_boundary (pos, dir)) < 0) {
907 d = ::llabs (pos - contender);
910 current_nearest = contender;
915 return current_nearest;
919 Editor::get_region_boundary (framepos_t pos, int32_t dir, bool with_selection, bool only_onscreen)
924 if (with_selection && Config->get_region_boundaries_from_selected_tracks()) {
926 if (!selection->tracks.empty()) {
928 target = find_next_region_boundary (pos, dir, selection->tracks);
932 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
933 get_onscreen_tracks (tvl);
934 target = find_next_region_boundary (pos, dir, tvl);
936 target = find_next_region_boundary (pos, dir, track_views);
942 if (only_onscreen || Config->get_region_boundaries_from_onscreen_tracks()) {
943 get_onscreen_tracks (tvl);
944 target = find_next_region_boundary (pos, dir, tvl);
946 target = find_next_region_boundary (pos, dir, track_views);
954 Editor::cursor_to_region_boundary (bool with_selection, int32_t dir)
956 framepos_t pos = playhead_cursor->current_frame ();
963 // so we don't find the current region again..
964 if (dir > 0 || pos > 0) {
968 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
972 _session->request_locate (target);
976 Editor::cursor_to_next_region_boundary (bool with_selection)
978 cursor_to_region_boundary (with_selection, 1);
982 Editor::cursor_to_previous_region_boundary (bool with_selection)
984 cursor_to_region_boundary (with_selection, -1);
988 Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t dir)
990 boost::shared_ptr<Region> r;
991 framepos_t pos = cursor->current_frame ();
997 TimeAxisView *ontrack = 0;
999 // so we don't find the current region again..
1003 if (!selection->tracks.empty()) {
1005 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1007 } else if (clicked_axisview) {
1010 t.push_back (clicked_axisview);
1012 r = find_next_region (pos, point, dir, t, &ontrack);
1016 r = find_next_region (pos, point, dir, track_views, &ontrack);
1025 pos = r->first_frame ();
1029 pos = r->last_frame ();
1033 pos = r->sync_position ();
1038 RouteTimeAxisView *rtav;
1040 if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
1041 if (rtav->track() != 0) {
1042 speed = rtav->track()->speed();
1046 pos = track_frame_to_session_frame(pos, speed);
1048 if (cursor == playhead_cursor) {
1049 _session->request_locate (pos);
1051 cursor->set_position (pos);
1056 Editor::cursor_to_next_region_point (EditorCursor* cursor, RegionPoint point)
1058 cursor_to_region_point (cursor, point, 1);
1062 Editor::cursor_to_previous_region_point (EditorCursor* cursor, RegionPoint point)
1064 cursor_to_region_point (cursor, point, -1);
1068 Editor::cursor_to_selection_start (EditorCursor *cursor)
1072 switch (mouse_mode) {
1074 if (!selection->regions.empty()) {
1075 pos = selection->regions.start();
1080 if (!selection->time.empty()) {
1081 pos = selection->time.start ();
1089 if (cursor == playhead_cursor) {
1090 _session->request_locate (pos);
1092 cursor->set_position (pos);
1097 Editor::cursor_to_selection_end (EditorCursor *cursor)
1101 switch (mouse_mode) {
1103 if (!selection->regions.empty()) {
1104 pos = selection->regions.end_frame();
1109 if (!selection->time.empty()) {
1110 pos = selection->time.end_frame ();
1118 if (cursor == playhead_cursor) {
1119 _session->request_locate (pos);
1121 cursor->set_position (pos);
1126 Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
1136 if (selection->markers.empty()) {
1140 if (!mouse_frame (mouse, ignored)) {
1144 add_location_mark (mouse);
1147 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1151 framepos_t pos = loc->start();
1153 // so we don't find the current region again..
1154 if (dir > 0 || pos > 0) {
1158 if ((target = get_region_boundary (pos, dir, with_selection, false)) < 0) {
1162 loc->move_to (target);
1166 Editor::selected_marker_to_next_region_boundary (bool with_selection)
1168 selected_marker_to_region_boundary (with_selection, 1);
1172 Editor::selected_marker_to_previous_region_boundary (bool with_selection)
1174 selected_marker_to_region_boundary (with_selection, -1);
1178 Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
1180 boost::shared_ptr<Region> r;
1185 if (!_session || selection->markers.empty()) {
1189 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1193 TimeAxisView *ontrack = 0;
1197 // so we don't find the current region again..
1201 if (!selection->tracks.empty()) {
1203 r = find_next_region (pos, point, dir, selection->tracks, &ontrack);
1207 r = find_next_region (pos, point, dir, track_views, &ontrack);
1216 pos = r->first_frame ();
1220 pos = r->last_frame ();
1224 pos = r->adjust_to_sync (r->first_frame());
1229 RouteTimeAxisView *rtav;
1231 if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
1232 if (rtav->track() != 0) {
1233 speed = rtav->track()->speed();
1237 pos = track_frame_to_session_frame(pos, speed);
1243 Editor::selected_marker_to_next_region_point (RegionPoint point)
1245 selected_marker_to_region_point (point, 1);
1249 Editor::selected_marker_to_previous_region_point (RegionPoint point)
1251 selected_marker_to_region_point (point, -1);
1255 Editor::selected_marker_to_selection_start ()
1261 if (!_session || selection->markers.empty()) {
1265 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1269 switch (mouse_mode) {
1271 if (!selection->regions.empty()) {
1272 pos = selection->regions.start();
1277 if (!selection->time.empty()) {
1278 pos = selection->time.start ();
1290 Editor::selected_marker_to_selection_end ()
1296 if (!_session || selection->markers.empty()) {
1300 if ((loc = find_location_from_marker (selection->markers.front(), ignored)) == 0) {
1304 switch (mouse_mode) {
1306 if (!selection->regions.empty()) {
1307 pos = selection->regions.end_frame();
1312 if (!selection->time.empty()) {
1313 pos = selection->time.end_frame ();
1325 Editor::scroll_playhead (bool forward)
1327 framepos_t pos = playhead_cursor->current_frame ();
1328 framecnt_t delta = (framecnt_t) floor (current_page_samples() / 0.8);
1331 if (pos == max_framepos) {
1335 if (pos < max_framepos - delta) {
1354 _session->request_locate (pos);
1358 Editor::cursor_align (bool playhead_to_edit)
1364 if (playhead_to_edit) {
1366 if (selection->markers.empty()) {
1370 _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
1373 /* move selected markers to playhead */
1375 for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
1378 Location* loc = find_location_from_marker (*i, ignored);
1380 if (loc->is_mark()) {
1381 loc->set_start (playhead_cursor->current_frame ());
1383 loc->set (playhead_cursor->current_frame (),
1384 playhead_cursor->current_frame () + loc->length());
1391 Editor::scroll_backward (float pages)
1393 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1394 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1397 if (leftmost_frame < cnt) {
1400 frame = leftmost_frame - cnt;
1403 reset_x_origin (frame);
1407 Editor::scroll_forward (float pages)
1409 framepos_t const one_page = (framepos_t) rint (_visible_canvas_width * samples_per_pixel);
1410 framepos_t const cnt = (framepos_t) floor (pages * one_page);
1413 if (max_framepos - cnt < leftmost_frame) {
1414 frame = max_framepos - cnt;
1416 frame = leftmost_frame + cnt;
1419 reset_x_origin (frame);
1423 Editor::scroll_tracks_down ()
1425 double vert_value = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
1426 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1427 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1430 vertical_adjustment.set_value (vert_value);
1434 Editor::scroll_tracks_up ()
1436 vertical_adjustment.set_value (vertical_adjustment.get_value() - vertical_adjustment.get_page_size());
1440 Editor::scroll_tracks_down_line ()
1442 double vert_value = vertical_adjustment.get_value() + 60;
1444 if (vert_value > vertical_adjustment.get_upper() - _visible_canvas_height) {
1445 vert_value = vertical_adjustment.get_upper() - _visible_canvas_height;
1448 vertical_adjustment.set_value (vert_value);
1452 Editor::scroll_tracks_up_line ()
1454 reset_y_origin (vertical_adjustment.get_value() - 60);
1458 Editor::scroll_down_one_track (bool skip_child_views)
1460 TrackViewList::reverse_iterator next = track_views.rend();
1461 const double top_of_trackviews = vertical_adjustment.get_value();
1463 for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
1464 if ((*t)->hidden()) {
1468 /* If this is the upper-most visible trackview, we want to display
1469 * the one above it (next)
1471 * Note that covers_y_position() is recursive and includes child views
1473 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1476 if (skip_child_views) {
1479 /* automation lane (one level, non-recursive)
1481 * - if no automation lane exists -> move to next tack
1482 * - if the first (here: bottom-most) matches -> move to next tack
1483 * - if no y-axis match is found -> the current track is at the top
1484 * -> move to last (here: top-most) automation lane
1486 TimeAxisView::Children kids = (*t)->get_child_list();
1487 TimeAxisView::Children::reverse_iterator nkid = kids.rend();
1489 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1490 if ((*ci)->hidden()) {
1494 std::pair<TimeAxisView*,double> dev;
1495 dev = (*ci)->covers_y_position (top_of_trackviews);
1497 /* some automation lane is currently at the top */
1498 if (ci == kids.rbegin()) {
1499 /* first (bottom-most) autmation lane is at the top.
1500 * -> move to next track
1509 if (nkid != kids.rend()) {
1510 ensure_time_axis_view_is_visible (**nkid, true);
1518 /* move to the track below the first one that covers the */
1520 if (next != track_views.rend()) {
1521 ensure_time_axis_view_is_visible (**next, true);
1529 Editor::scroll_up_one_track (bool skip_child_views)
1531 TrackViewList::iterator prev = track_views.end();
1532 double top_of_trackviews = vertical_adjustment.get_value ();
1534 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
1536 if ((*t)->hidden()) {
1540 /* find the trackview at the top of the trackview group
1542 * Note that covers_y_position() is recursive and includes child views
1544 std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
1547 if (skip_child_views) {
1550 /* automation lane (one level, non-recursive)
1552 * - if no automation lane exists -> move to prev tack
1553 * - if no y-axis match is found -> the current track is at the top -> move to prev track
1554 * (actually last automation lane of previous track, see below)
1555 * - if first (top-most) lane is at the top -> move to this track
1556 * - else move up one lane
1558 TimeAxisView::Children kids = (*t)->get_child_list();
1559 TimeAxisView::Children::iterator pkid = kids.end();
1561 for (TimeAxisView::Children::iterator ci = kids.begin(); ci != kids.end(); ++ci) {
1562 if ((*ci)->hidden()) {
1566 std::pair<TimeAxisView*,double> dev;
1567 dev = (*ci)->covers_y_position (top_of_trackviews);
1569 /* some automation lane is currently at the top */
1570 if (ci == kids.begin()) {
1571 /* first (top-most) autmation lane is at the top.
1572 * jump directly to this track's top
1574 ensure_time_axis_view_is_visible (**t, true);
1577 else if (pkid != kids.end()) {
1578 /* some other automation lane is at the top.
1579 * move up to prev automation lane.
1581 ensure_time_axis_view_is_visible (**pkid, true);
1584 assert(0); // not reached
1595 if (prev != track_views.end()) {
1596 // move to bottom-most automation-lane of the previous track
1597 TimeAxisView::Children kids = (*prev)->get_child_list();
1598 TimeAxisView::Children::reverse_iterator pkid = kids.rend();
1599 if (!skip_child_views) {
1600 // find the last visible lane
1601 for (TimeAxisView::Children::reverse_iterator ci = kids.rbegin(); ci != kids.rend(); ++ci) {
1602 if (!(*ci)->hidden()) {
1608 if (pkid != kids.rend()) {
1609 ensure_time_axis_view_is_visible (**pkid, true);
1611 ensure_time_axis_view_is_visible (**prev, true);
1620 Editor::scroll_left_step ()
1622 framepos_t xdelta = (current_page_samples() / 8);
1624 if (leftmost_frame > xdelta) {
1625 reset_x_origin (leftmost_frame - xdelta);
1633 Editor::scroll_right_step ()
1635 framepos_t xdelta = (current_page_samples() / 8);
1637 if (max_framepos - xdelta > leftmost_frame) {
1638 reset_x_origin (leftmost_frame + xdelta);
1640 reset_x_origin (max_framepos - current_page_samples());
1645 Editor::scroll_left_half_page ()
1647 framepos_t xdelta = (current_page_samples() / 2);
1648 if (leftmost_frame > xdelta) {
1649 reset_x_origin (leftmost_frame - xdelta);
1656 Editor::scroll_right_half_page ()
1658 framepos_t xdelta = (current_page_samples() / 2);
1659 if (max_framepos - xdelta > leftmost_frame) {
1660 reset_x_origin (leftmost_frame + xdelta);
1662 reset_x_origin (max_framepos - current_page_samples());
1669 Editor::tav_zoom_step (bool coarser)
1671 DisplaySuspender ds;
1675 if (selection->tracks.empty()) {
1678 ts = &selection->tracks;
1681 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1682 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1683 tv->step_height (coarser);
1688 Editor::tav_zoom_smooth (bool coarser, bool force_all)
1690 DisplaySuspender ds;
1694 if (selection->tracks.empty() || force_all) {
1697 ts = &selection->tracks;
1700 for (TrackViewList::iterator i = ts->begin(); i != ts->end(); ++i) {
1701 TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
1702 uint32_t h = tv->current_height ();
1707 if (h >= TimeAxisView::preset_height (HeightSmall)) {
1712 tv->set_height (h + 5);
1718 Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
1720 Editing::ZoomFocus temp_focus = zoom_focus;
1721 zoom_focus = Editing::ZoomFocusMouse;
1722 temporal_zoom_step_scale (zoom_out, scale);
1723 zoom_focus = temp_focus;
1727 Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
1729 temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
1733 Editor::temporal_zoom_step (bool zoom_out)
1735 temporal_zoom_step_scale (zoom_out, 2.0);
1739 Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
1741 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
1743 framecnt_t nspp = samples_per_pixel;
1747 if (nspp == samples_per_pixel) {
1752 if (nspp == samples_per_pixel) {
1757 temporal_zoom (nspp);
1761 Editor::temporal_zoom (framecnt_t fpp)
1767 framepos_t current_page = current_page_samples();
1768 framepos_t current_leftmost = leftmost_frame;
1769 framepos_t current_rightmost;
1770 framepos_t current_center;
1771 framepos_t new_page_size;
1772 framepos_t half_page_size;
1773 framepos_t leftmost_after_zoom = 0;
1775 bool in_track_canvas;
1776 bool use_mouse_frame = true;
1780 if (fpp == samples_per_pixel) {
1784 // Imposing an arbitrary limit to zoom out as too much zoom out produces
1785 // segfaults for lack of memory. If somebody decides this is not high enough I
1786 // believe it can be raisen to higher values but some limit must be in place.
1788 // This constant represents 1 day @ 48kHz on a 1600 pixel wide display
1789 // all of which is used for the editor track displays. The whole day
1790 // would be 4147200000 samples, so 2592000 samples per pixel.
1792 nfpp = min (fpp, (framecnt_t) 2592000);
1793 nfpp = max ((framecnt_t) 1, nfpp);
1795 new_page_size = (framepos_t) floor (_visible_canvas_width * nfpp);
1796 half_page_size = new_page_size / 2;
1798 switch (zoom_focus) {
1800 leftmost_after_zoom = current_leftmost;
1803 case ZoomFocusRight:
1804 current_rightmost = leftmost_frame + current_page;
1805 if (current_rightmost < new_page_size) {
1806 leftmost_after_zoom = 0;
1808 leftmost_after_zoom = current_rightmost - new_page_size;
1812 case ZoomFocusCenter:
1813 current_center = current_leftmost + (current_page/2);
1814 if (current_center < half_page_size) {
1815 leftmost_after_zoom = 0;
1817 leftmost_after_zoom = current_center - half_page_size;
1821 case ZoomFocusPlayhead:
1822 /* centre playhead */
1823 l = playhead_cursor->current_frame () - (new_page_size * 0.5);
1826 leftmost_after_zoom = 0;
1827 } else if (l > max_framepos) {
1828 leftmost_after_zoom = max_framepos - new_page_size;
1830 leftmost_after_zoom = (framepos_t) l;
1834 case ZoomFocusMouse:
1835 /* try to keep the mouse over the same point in the display */
1837 if (_drags->active()) {
1838 where = _drags->current_pointer_frame ();
1839 } else if (!mouse_frame (where, in_track_canvas)) {
1840 use_mouse_frame = false;
1843 if (use_mouse_frame) {
1844 l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1847 leftmost_after_zoom = 0;
1848 } else if (l > max_framepos) {
1849 leftmost_after_zoom = max_framepos - new_page_size;
1851 leftmost_after_zoom = (framepos_t) l;
1854 /* use playhead instead */
1855 where = playhead_cursor->current_frame ();
1857 if (where < half_page_size) {
1858 leftmost_after_zoom = 0;
1860 leftmost_after_zoom = where - half_page_size;
1866 /* try to keep the edit point in the same place */
1867 where = get_preferred_edit_position ();
1871 double l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
1874 leftmost_after_zoom = 0;
1875 } else if (l > max_framepos) {
1876 leftmost_after_zoom = max_framepos - new_page_size;
1878 leftmost_after_zoom = (framepos_t) l;
1882 /* edit point not defined */
1889 // leftmost_after_zoom = min (leftmost_after_zoom, _session->current_end_frame());
1891 reposition_and_zoom (leftmost_after_zoom, nfpp);
1895 Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
1897 /* this func helps make sure we leave a little space
1898 at each end of the editor so that the zoom doesn't fit the region
1899 precisely to the screen.
1902 GdkScreen* screen = gdk_screen_get_default ();
1903 const gint pixwidth = gdk_screen_get_width (screen);
1904 const gint mmwidth = gdk_screen_get_width_mm (screen);
1905 const double pix_per_mm = (double) pixwidth/ (double) mmwidth;
1906 const double one_centimeter_in_pixels = pix_per_mm * 10.0;
1908 const framepos_t range = end - start;
1909 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
1910 const framepos_t extra_samples = (framepos_t) floor (one_centimeter_in_pixels * new_fpp);
1912 if (start > extra_samples) {
1913 start -= extra_samples;
1918 if (max_framepos - extra_samples > end) {
1919 end += extra_samples;
1926 Editor::temporal_zoom_region (bool both_axes)
1928 framepos_t start = max_framepos;
1930 set<TimeAxisView*> tracks;
1932 if ( !get_selection_extents(start, end) )
1935 calc_extra_zoom_edges (start, end);
1937 /* if we're zooming on both axes we need to save track heights etc.
1940 undo_visual_stack.push_back (current_visual_state (both_axes));
1942 PBD::Unwinder<bool> nsv (no_save_visual, true);
1944 temporal_zoom_by_frame (start, end);
1947 uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
1949 /* set visible track heights appropriately */
1951 for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
1952 (*t)->set_height (per_track_height);
1955 /* hide irrelevant tracks */
1957 DisplaySuspender ds;
1959 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1960 if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
1961 hide_track_in_display (*i);
1965 vertical_adjustment.set_value (0.0);
1968 redo_visual_stack.push_back (current_visual_state (both_axes));
1973 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
1975 start = max_framepos;
1979 //ToDo: if notes are selected, set extents to that selection
1981 //ToDo: if control points are selected, set extents to that selection
1983 if ( !selection->regions.empty() ) {
1984 RegionSelection rs = get_regions_from_selection_and_entered ();
1986 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1988 if ((*i)->region()->position() < start) {
1989 start = (*i)->region()->position();
1992 if ((*i)->region()->last_frame() + 1 > end) {
1993 end = (*i)->region()->last_frame() + 1;
1997 } else if (!selection->time.empty()) {
1998 start = selection->time.start();
1999 end = selection->time.end_frame();
2001 ret = false; //no selection found
2004 if ((start == 0 && end == 0) || end < start) {
2013 Editor::temporal_zoom_selection (bool both_axes)
2015 if (!selection) return;
2017 //ToDo: if notes are selected, zoom to that
2019 //ToDo: if control points are selected, zoom to that
2021 //if region(s) are selected, zoom to that
2022 if ( !selection->regions.empty() )
2023 temporal_zoom_region (both_axes);
2025 //if a range is selected, zoom to that
2026 if (!selection->time.empty()) {
2028 framepos_t start, end;
2029 if (get_selection_extents (start, end)) {
2030 calc_extra_zoom_edges(start, end);
2031 temporal_zoom_by_frame (start, end);
2041 Editor::temporal_zoom_session ()
2043 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
2046 framecnt_t start = _session->current_start_frame();
2047 framecnt_t end = _session->current_end_frame();
2049 if (_session->actively_recording () ) {
2050 framepos_t cur = playhead_cursor->current_frame ();
2052 /* recording beyond the end marker; zoom out
2053 * by 5 seconds more so that if 'follow
2054 * playhead' is active we don't immediately
2057 end = cur + _session->frame_rate() * 5;
2061 if ((start == 0 && end == 0) || end < start) {
2065 calc_extra_zoom_edges(start, end);
2067 temporal_zoom_by_frame (start, end);
2072 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2074 if (!_session) return;
2076 if ((start == 0 && end == 0) || end < start) {
2080 framepos_t range = end - start;
2082 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2084 framepos_t new_page = range;
2085 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2086 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2088 if (new_leftmost > middle) {
2092 if (new_leftmost < 0) {
2096 reposition_and_zoom (new_leftmost, new_fpp);
2100 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2106 framecnt_t range_before = frame - leftmost_frame;
2110 if (samples_per_pixel <= 1) {
2113 new_spp = samples_per_pixel + (samples_per_pixel/2);
2115 range_before += range_before/2;
2117 if (samples_per_pixel >= 1) {
2118 new_spp = samples_per_pixel - (samples_per_pixel/2);
2120 /* could bail out here since we cannot zoom any finer,
2121 but leave that to the equality test below
2123 new_spp = samples_per_pixel;
2126 range_before -= range_before/2;
2129 if (new_spp == samples_per_pixel) {
2133 /* zoom focus is automatically taken as @param frame when this
2137 framepos_t new_leftmost = frame - (framepos_t)range_before;
2139 if (new_leftmost > frame) {
2143 if (new_leftmost < 0) {
2147 reposition_and_zoom (new_leftmost, new_spp);
2152 Editor::choose_new_marker_name(string &name) {
2154 if (!UIConfiguration::instance().get_name_new_markers()) {
2155 /* don't prompt user for a new name */
2159 ArdourPrompter dialog (true);
2161 dialog.set_prompt (_("New Name:"));
2163 dialog.set_title (_("New Location Marker"));
2165 dialog.set_name ("MarkNameWindow");
2166 dialog.set_size_request (250, -1);
2167 dialog.set_position (Gtk::WIN_POS_MOUSE);
2169 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2170 dialog.set_initial_text (name);
2174 switch (dialog.run ()) {
2175 case RESPONSE_ACCEPT:
2181 dialog.get_result(name);
2188 Editor::add_location_from_selection ()
2192 if (selection->time.empty()) {
2196 if (_session == 0 || clicked_axisview == 0) {
2200 framepos_t start = selection->time[clicked_selection].start;
2201 framepos_t end = selection->time[clicked_selection].end;
2203 _session->locations()->next_available_name(rangename,"selection");
2204 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2206 begin_reversible_command (_("add marker"));
2208 XMLNode &before = _session->locations()->get_state();
2209 _session->locations()->add (location, true);
2210 XMLNode &after = _session->locations()->get_state();
2211 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2213 commit_reversible_command ();
2217 Editor::add_location_mark (framepos_t where)
2221 select_new_marker = true;
2223 _session->locations()->next_available_name(markername,"mark");
2224 if (!choose_new_marker_name(markername)) {
2227 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2228 begin_reversible_command (_("add marker"));
2230 XMLNode &before = _session->locations()->get_state();
2231 _session->locations()->add (location, true);
2232 XMLNode &after = _session->locations()->get_state();
2233 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2235 commit_reversible_command ();
2239 Editor::set_session_start_from_playhead ()
2245 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2246 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2248 XMLNode &before = loc->get_state();
2250 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2252 XMLNode &after = loc->get_state();
2254 begin_reversible_command (_("Set session start"));
2256 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2258 commit_reversible_command ();
2263 Editor::set_session_end_from_playhead ()
2269 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2270 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2272 XMLNode &before = loc->get_state();
2274 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2276 XMLNode &after = loc->get_state();
2278 begin_reversible_command (_("Set session start"));
2280 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2282 commit_reversible_command ();
2285 _session->set_end_is_free (false);
2290 Editor::toggle_location_at_playhead_cursor ()
2292 if (!do_remove_location_at_playhead_cursor())
2294 add_location_from_playhead_cursor();
2299 Editor::add_location_from_playhead_cursor ()
2301 add_location_mark (_session->audible_frame());
2305 Editor::do_remove_location_at_playhead_cursor ()
2307 bool removed = false;
2310 XMLNode &before = _session->locations()->get_state();
2312 //find location(s) at this time
2313 Locations::LocationList locs;
2314 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2315 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2316 if ((*i)->is_mark()) {
2317 _session->locations()->remove (*i);
2324 begin_reversible_command (_("remove marker"));
2325 XMLNode &after = _session->locations()->get_state();
2326 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2327 commit_reversible_command ();
2334 Editor::remove_location_at_playhead_cursor ()
2336 do_remove_location_at_playhead_cursor ();
2339 /** Add a range marker around each selected region */
2341 Editor::add_locations_from_region ()
2343 RegionSelection rs = get_regions_from_selection_and_entered ();
2348 bool commit = false;
2350 XMLNode &before = _session->locations()->get_state();
2352 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2354 boost::shared_ptr<Region> region = (*i)->region ();
2356 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2358 _session->locations()->add (location, true);
2363 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2364 XMLNode &after = _session->locations()->get_state();
2365 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2366 commit_reversible_command ();
2370 /** Add a single range marker around all selected regions */
2372 Editor::add_location_from_region ()
2374 RegionSelection rs = get_regions_from_selection_and_entered ();
2380 XMLNode &before = _session->locations()->get_state();
2384 if (rs.size() > 1) {
2385 _session->locations()->next_available_name(markername, "regions");
2387 RegionView* rv = *(rs.begin());
2388 boost::shared_ptr<Region> region = rv->region();
2389 markername = region->name();
2392 if (!choose_new_marker_name(markername)) {
2396 // single range spanning all selected
2397 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2398 _session->locations()->add (location, true);
2400 begin_reversible_command (_("add marker"));
2401 XMLNode &after = _session->locations()->get_state();
2402 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2403 commit_reversible_command ();
2409 Editor::jump_forward_to_mark ()
2415 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2421 _session->request_locate (pos, _session->transport_rolling());
2425 Editor::jump_backward_to_mark ()
2431 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2437 _session->request_locate (pos, _session->transport_rolling());
2443 framepos_t const pos = _session->audible_frame ();
2446 _session->locations()->next_available_name (markername, "mark");
2448 if (!choose_new_marker_name (markername)) {
2452 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2456 Editor::clear_markers ()
2459 begin_reversible_command (_("clear markers"));
2461 XMLNode &before = _session->locations()->get_state();
2462 _session->locations()->clear_markers ();
2463 XMLNode &after = _session->locations()->get_state();
2464 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2466 commit_reversible_command ();
2471 Editor::clear_ranges ()
2474 begin_reversible_command (_("clear ranges"));
2476 XMLNode &before = _session->locations()->get_state();
2478 _session->locations()->clear_ranges ();
2480 XMLNode &after = _session->locations()->get_state();
2481 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2483 commit_reversible_command ();
2488 Editor::clear_locations ()
2490 begin_reversible_command (_("clear locations"));
2492 XMLNode &before = _session->locations()->get_state();
2493 _session->locations()->clear ();
2494 XMLNode &after = _session->locations()->get_state();
2495 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2497 commit_reversible_command ();
2501 Editor::unhide_markers ()
2503 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2504 Location *l = (*i).first;
2505 if (l->is_hidden() && l->is_mark()) {
2506 l->set_hidden(false, this);
2512 Editor::unhide_ranges ()
2514 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2515 Location *l = (*i).first;
2516 if (l->is_hidden() && l->is_range_marker()) {
2517 l->set_hidden(false, this);
2522 /* INSERT/REPLACE */
2525 Editor::insert_region_list_selection (float times)
2527 RouteTimeAxisView *tv = 0;
2528 boost::shared_ptr<Playlist> playlist;
2530 if (clicked_routeview != 0) {
2531 tv = clicked_routeview;
2532 } else if (!selection->tracks.empty()) {
2533 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2536 } else if (entered_track != 0) {
2537 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2544 if ((playlist = tv->playlist()) == 0) {
2548 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2553 begin_reversible_command (_("insert region"));
2554 playlist->clear_changes ();
2555 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2556 if (Config->get_edit_mode() == Ripple)
2557 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2559 _session->add_command(new StatefulDiffCommand (playlist));
2560 commit_reversible_command ();
2563 /* BUILT-IN EFFECTS */
2566 Editor::reverse_selection ()
2571 /* GAIN ENVELOPE EDITING */
2574 Editor::edit_envelope ()
2581 Editor::transition_to_rolling (bool fwd)
2587 if (_session->config.get_external_sync()) {
2588 switch (Config->get_sync_source()) {
2592 /* transport controlled by the master */
2597 if (_session->is_auditioning()) {
2598 _session->cancel_audition ();
2602 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2606 Editor::play_from_start ()
2608 _session->request_locate (_session->current_start_frame(), true);
2612 Editor::play_from_edit_point ()
2614 _session->request_locate (get_preferred_edit_position(), true);
2618 Editor::play_from_edit_point_and_return ()
2620 framepos_t start_frame;
2621 framepos_t return_frame;
2623 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2625 if (_session->transport_rolling()) {
2626 _session->request_locate (start_frame, false);
2630 /* don't reset the return frame if its already set */
2632 if ((return_frame = _session->requested_return_frame()) < 0) {
2633 return_frame = _session->audible_frame();
2636 if (start_frame >= 0) {
2637 _session->request_roll_at_and_return (start_frame, return_frame);
2642 Editor::play_selection ()
2644 framepos_t start, end;
2645 if (!get_selection_extents ( start, end))
2648 AudioRange ar (start, end, 0);
2649 list<AudioRange> lar;
2652 _session->request_play_range (&lar, true);
2656 Editor::get_preroll ()
2658 return Config->get_preroll_seconds() * _session->frame_rate();
2663 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2665 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2668 location -= get_preroll();
2670 //don't try to locate before the beginning of time
2674 //if follow_playhead is on, keep the playhead on the screen
2675 if ( _follow_playhead )
2676 if ( location < leftmost_frame )
2677 location = leftmost_frame;
2679 _session->request_locate( location );
2683 Editor::play_with_preroll ()
2686 framepos_t preroll = get_preroll();
2688 framepos_t start, end;
2689 if (!get_selection_extents ( start, end))
2692 if (start > preroll)
2693 start = start - preroll;
2695 end = end + preroll; //"post-roll"
2697 AudioRange ar (start, end, 0);
2698 list<AudioRange> lar;
2701 _session->request_play_range (&lar, true);
2706 Editor::play_location (Location& location)
2708 if (location.start() <= location.end()) {
2712 _session->request_bounded_roll (location.start(), location.end());
2716 Editor::loop_location (Location& location)
2718 if (location.start() <= location.end()) {
2724 if ((tll = transport_loop_location()) != 0) {
2725 tll->set (location.start(), location.end());
2727 // enable looping, reposition and start rolling
2728 _session->request_locate (tll->start(), true);
2729 _session->request_play_loop (true);
2734 Editor::do_layer_operation (LayerOperation op)
2736 if (selection->regions.empty ()) {
2740 bool const multiple = selection->regions.size() > 1;
2744 begin_reversible_command (_("raise regions"));
2746 begin_reversible_command (_("raise region"));
2752 begin_reversible_command (_("raise regions to top"));
2754 begin_reversible_command (_("raise region to top"));
2760 begin_reversible_command (_("lower regions"));
2762 begin_reversible_command (_("lower region"));
2768 begin_reversible_command (_("lower regions to bottom"));
2770 begin_reversible_command (_("lower region"));
2775 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2776 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2777 (*i)->clear_owned_changes ();
2780 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2781 boost::shared_ptr<Region> r = (*i)->region ();
2793 r->lower_to_bottom ();
2797 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2798 vector<Command*> cmds;
2800 _session->add_commands (cmds);
2803 commit_reversible_command ();
2807 Editor::raise_region ()
2809 do_layer_operation (Raise);
2813 Editor::raise_region_to_top ()
2815 do_layer_operation (RaiseToTop);
2819 Editor::lower_region ()
2821 do_layer_operation (Lower);
2825 Editor::lower_region_to_bottom ()
2827 do_layer_operation (LowerToBottom);
2830 /** Show the region editor for the selected regions */
2832 Editor::show_region_properties ()
2834 selection->foreach_regionview (&RegionView::show_region_editor);
2837 /** Show the midi list editor for the selected MIDI regions */
2839 Editor::show_midi_list_editor ()
2841 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2845 Editor::rename_region ()
2847 RegionSelection rs = get_regions_from_selection_and_entered ();
2853 ArdourDialog d (_("Rename Region"), true, false);
2855 Label label (_("New name:"));
2858 hbox.set_spacing (6);
2859 hbox.pack_start (label, false, false);
2860 hbox.pack_start (entry, true, true);
2862 d.get_vbox()->set_border_width (12);
2863 d.get_vbox()->pack_start (hbox, false, false);
2865 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2866 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2868 d.set_size_request (300, -1);
2870 entry.set_text (rs.front()->region()->name());
2871 entry.select_region (0, -1);
2873 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2879 int const ret = d.run();
2883 if (ret != RESPONSE_OK) {
2887 std::string str = entry.get_text();
2888 strip_whitespace_edges (str);
2890 rs.front()->region()->set_name (str);
2891 _regions->redisplay ();
2895 /** Start an audition of the first selected region */
2897 Editor::play_edit_range ()
2899 framepos_t start, end;
2901 if (get_edit_op_range (start, end)) {
2902 _session->request_bounded_roll (start, end);
2907 Editor::play_selected_region ()
2909 framepos_t start = max_framepos;
2912 RegionSelection rs = get_regions_from_selection_and_entered ();
2918 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2919 if ((*i)->region()->position() < start) {
2920 start = (*i)->region()->position();
2922 if ((*i)->region()->last_frame() + 1 > end) {
2923 end = (*i)->region()->last_frame() + 1;
2927 _session->request_bounded_roll (start, end);
2931 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2933 _session->audition_region (region);
2937 Editor::region_from_selection ()
2939 if (clicked_axisview == 0) {
2943 if (selection->time.empty()) {
2947 framepos_t start = selection->time[clicked_selection].start;
2948 framepos_t end = selection->time[clicked_selection].end;
2950 TrackViewList tracks = get_tracks_for_range_action ();
2952 framepos_t selection_cnt = end - start + 1;
2954 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2955 boost::shared_ptr<Region> current;
2956 boost::shared_ptr<Playlist> pl;
2957 framepos_t internal_start;
2960 if ((pl = (*i)->playlist()) == 0) {
2964 if ((current = pl->top_region_at (start)) == 0) {
2968 internal_start = start - current->position();
2969 RegionFactory::region_name (new_name, current->name(), true);
2973 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2974 plist.add (ARDOUR::Properties::length, selection_cnt);
2975 plist.add (ARDOUR::Properties::name, new_name);
2976 plist.add (ARDOUR::Properties::layer, 0);
2978 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2983 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2985 if (selection->time.empty() || selection->tracks.empty()) {
2989 framepos_t start, end;
2990 if (clicked_selection) {
2991 start = selection->time[clicked_selection].start;
2992 end = selection->time[clicked_selection].end;
2994 start = selection->time.start();
2995 end = selection->time.end_frame();
2998 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2999 sort_track_selection (ts);
3001 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3002 boost::shared_ptr<Region> current;
3003 boost::shared_ptr<Playlist> playlist;
3004 framepos_t internal_start;
3007 if ((playlist = (*i)->playlist()) == 0) {
3011 if ((current = playlist->top_region_at(start)) == 0) {
3015 internal_start = start - current->position();
3016 RegionFactory::region_name (new_name, current->name(), true);
3020 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
3021 plist.add (ARDOUR::Properties::length, end - start + 1);
3022 plist.add (ARDOUR::Properties::name, new_name);
3024 new_regions.push_back (RegionFactory::create (current, plist));
3029 Editor::split_multichannel_region ()
3031 RegionSelection rs = get_regions_from_selection_and_entered ();
3037 vector< boost::shared_ptr<Region> > v;
3039 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
3040 (*x)->region()->separate_by_channel (*_session, v);
3045 Editor::new_region_from_selection ()
3047 region_from_selection ();
3048 cancel_selection ();
3052 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3054 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3055 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3056 case Evoral::OverlapNone:
3064 * - selected tracks, or if there are none...
3065 * - tracks containing selected regions, or if there are none...
3070 Editor::get_tracks_for_range_action () const
3074 if (selection->tracks.empty()) {
3076 /* use tracks with selected regions */
3078 RegionSelection rs = selection->regions;
3080 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3081 TimeAxisView* tv = &(*i)->get_time_axis_view();
3083 if (!t.contains (tv)) {
3089 /* no regions and no tracks: use all tracks */
3095 t = selection->tracks;
3098 return t.filter_to_unique_playlists();
3102 Editor::separate_regions_between (const TimeSelection& ts)
3104 bool in_command = false;
3105 boost::shared_ptr<Playlist> playlist;
3106 RegionSelection new_selection;
3108 TrackViewList tmptracks = get_tracks_for_range_action ();
3109 sort_track_selection (tmptracks);
3111 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3113 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3119 if (!rtv->is_track()) {
3123 /* no edits to destructive tracks */
3125 if (rtv->track()->destructive()) {
3129 if ((playlist = rtv->playlist()) != 0) {
3131 playlist->clear_changes ();
3133 /* XXX need to consider musical time selections here at some point */
3135 double speed = rtv->track()->speed();
3137 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3139 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3140 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3142 latest_regionviews.clear ();
3144 playlist->partition ((framepos_t)((*t).start * speed),
3145 (framepos_t)((*t).end * speed), false);
3149 if (!latest_regionviews.empty()) {
3151 rtv->view()->foreach_regionview (sigc::bind (
3152 sigc::ptr_fun (add_if_covered),
3153 &(*t), &new_selection));
3156 begin_reversible_command (_("separate"));
3160 /* pick up changes to existing regions */
3162 vector<Command*> cmds;
3163 playlist->rdiff (cmds);
3164 _session->add_commands (cmds);
3166 /* pick up changes to the playlist itself (adds/removes)
3169 _session->add_command(new StatefulDiffCommand (playlist));
3176 // selection->set (new_selection);
3178 commit_reversible_command ();
3182 struct PlaylistState {
3183 boost::shared_ptr<Playlist> playlist;
3187 /** Take tracks from get_tracks_for_range_action and cut any regions
3188 * on those tracks so that the tracks are empty over the time
3192 Editor::separate_region_from_selection ()
3194 /* preferentially use *all* ranges in the time selection if we're in range mode
3195 to allow discontiguous operation, since get_edit_op_range() currently
3196 returns a single range.
3199 if (!selection->time.empty()) {
3201 separate_regions_between (selection->time);
3208 if (get_edit_op_range (start, end)) {
3210 AudioRange ar (start, end, 1);
3214 separate_regions_between (ts);
3220 Editor::separate_region_from_punch ()
3222 Location* loc = _session->locations()->auto_punch_location();
3224 separate_regions_using_location (*loc);
3229 Editor::separate_region_from_loop ()
3231 Location* loc = _session->locations()->auto_loop_location();
3233 separate_regions_using_location (*loc);
3238 Editor::separate_regions_using_location (Location& loc)
3240 if (loc.is_mark()) {
3244 AudioRange ar (loc.start(), loc.end(), 1);
3249 separate_regions_between (ts);
3252 /** Separate regions under the selected region */
3254 Editor::separate_under_selected_regions ()
3256 vector<PlaylistState> playlists;
3260 rs = get_regions_from_selection_and_entered();
3262 if (!_session || rs.empty()) {
3266 begin_reversible_command (_("separate region under"));
3268 list<boost::shared_ptr<Region> > regions_to_remove;
3270 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3271 // we can't just remove the region(s) in this loop because
3272 // this removes them from the RegionSelection, and they thus
3273 // disappear from underneath the iterator, and the ++i above
3274 // SEGVs in a puzzling fashion.
3276 // so, first iterate over the regions to be removed from rs and
3277 // add them to the regions_to_remove list, and then
3278 // iterate over the list to actually remove them.
3280 regions_to_remove.push_back ((*i)->region());
3283 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3285 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3288 // is this check necessary?
3292 vector<PlaylistState>::iterator i;
3294 //only take state if this is a new playlist.
3295 for (i = playlists.begin(); i != playlists.end(); ++i) {
3296 if ((*i).playlist == playlist) {
3301 if (i == playlists.end()) {
3303 PlaylistState before;
3304 before.playlist = playlist;
3305 before.before = &playlist->get_state();
3307 playlist->freeze ();
3308 playlists.push_back(before);
3311 //Partition on the region bounds
3312 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3314 //Re-add region that was just removed due to the partition operation
3315 playlist->add_region( (*rl), (*rl)->first_frame() );
3318 vector<PlaylistState>::iterator pl;
3320 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3321 (*pl).playlist->thaw ();
3322 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3325 commit_reversible_command ();
3329 Editor::crop_region_to_selection ()
3331 if (!selection->time.empty()) {
3333 crop_region_to (selection->time.start(), selection->time.end_frame());
3340 if (get_edit_op_range (start, end)) {
3341 crop_region_to (start, end);
3348 Editor::crop_region_to (framepos_t start, framepos_t end)
3350 vector<boost::shared_ptr<Playlist> > playlists;
3351 boost::shared_ptr<Playlist> playlist;
3354 if (selection->tracks.empty()) {
3355 ts = track_views.filter_to_unique_playlists();
3357 ts = selection->tracks.filter_to_unique_playlists ();
3360 sort_track_selection (ts);
3362 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3364 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3370 boost::shared_ptr<Track> t = rtv->track();
3372 if (t != 0 && ! t->destructive()) {
3374 if ((playlist = rtv->playlist()) != 0) {
3375 playlists.push_back (playlist);
3380 if (playlists.empty()) {
3385 framepos_t new_start;
3387 framecnt_t new_length;
3388 bool in_command = false;
3390 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3392 /* Only the top regions at start and end have to be cropped */
3393 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3394 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3396 vector<boost::shared_ptr<Region> > regions;
3398 if (region_at_start != 0) {
3399 regions.push_back (region_at_start);
3401 if (region_at_end != 0) {
3402 regions.push_back (region_at_end);
3405 /* now adjust lengths */
3406 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3408 pos = (*i)->position();
3409 new_start = max (start, pos);
3410 if (max_framepos - pos > (*i)->length()) {
3411 new_end = pos + (*i)->length() - 1;
3413 new_end = max_framepos;
3415 new_end = min (end, new_end);
3416 new_length = new_end - new_start + 1;
3419 begin_reversible_command (_("trim to selection"));
3422 (*i)->clear_changes ();
3423 (*i)->trim_to (new_start, new_length);
3424 _session->add_command (new StatefulDiffCommand (*i));
3429 commit_reversible_command ();
3434 Editor::region_fill_track ()
3436 boost::shared_ptr<Playlist> playlist;
3437 RegionSelection regions = get_regions_from_selection_and_entered ();
3438 RegionSelection foo;
3440 framepos_t const end = _session->current_end_frame ();
3442 if (regions.empty () || regions.end_frame () + 1 >= end) {
3446 framepos_t const start_frame = regions.start ();
3447 framepos_t const end_frame = regions.end_frame ();
3448 framecnt_t const gap = end_frame - start_frame + 1;
3450 begin_reversible_command (Operations::region_fill);
3452 selection->clear_regions ();
3454 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3456 boost::shared_ptr<Region> r ((*i)->region());
3458 TimeAxisView& tv = (*i)->get_time_axis_view();
3459 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3460 latest_regionviews.clear ();
3461 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3463 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3464 playlist = (*i)->region()->playlist();
3465 playlist->clear_changes ();
3466 playlist->duplicate_until (r, position, gap, end);
3467 _session->add_command(new StatefulDiffCommand (playlist));
3471 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3475 selection->set (foo);
3478 commit_reversible_command ();
3482 Editor::set_region_sync_position ()
3484 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3488 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3490 bool in_command = false;
3492 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3494 if (!(*r)->region()->covers (where)) {
3498 boost::shared_ptr<Region> region ((*r)->region());
3501 begin_reversible_command (_("set sync point"));
3505 region->clear_changes ();
3506 region->set_sync_position (where);
3507 _session->add_command(new StatefulDiffCommand (region));
3511 commit_reversible_command ();
3515 /** Remove the sync positions of the selection */
3517 Editor::remove_region_sync ()
3519 RegionSelection rs = get_regions_from_selection_and_entered ();
3525 begin_reversible_command (_("remove region sync"));
3527 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3529 (*i)->region()->clear_changes ();
3530 (*i)->region()->clear_sync_position ();
3531 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3534 commit_reversible_command ();
3538 Editor::naturalize_region ()
3540 RegionSelection rs = get_regions_from_selection_and_entered ();
3546 if (rs.size() > 1) {
3547 begin_reversible_command (_("move regions to original position"));
3549 begin_reversible_command (_("move region to original position"));
3552 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3553 (*i)->region()->clear_changes ();
3554 (*i)->region()->move_to_natural_position ();
3555 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3558 commit_reversible_command ();
3562 Editor::align_regions (RegionPoint what)
3564 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3570 begin_reversible_command (_("align selection"));
3572 framepos_t const position = get_preferred_edit_position ();
3574 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3575 align_region_internal ((*i)->region(), what, position);
3578 commit_reversible_command ();
3581 struct RegionSortByTime {
3582 bool operator() (const RegionView* a, const RegionView* b) {
3583 return a->region()->position() < b->region()->position();
3588 Editor::align_regions_relative (RegionPoint point)
3590 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3596 framepos_t const position = get_preferred_edit_position ();
3598 framepos_t distance = 0;
3602 list<RegionView*> sorted;
3603 rs.by_position (sorted);
3605 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3610 if (position > r->position()) {
3611 distance = position - r->position();
3613 distance = r->position() - position;
3619 if (position > r->last_frame()) {
3620 distance = position - r->last_frame();
3621 pos = r->position() + distance;
3623 distance = r->last_frame() - position;
3624 pos = r->position() - distance;
3630 pos = r->adjust_to_sync (position);
3631 if (pos > r->position()) {
3632 distance = pos - r->position();
3634 distance = r->position() - pos;
3640 if (pos == r->position()) {
3644 begin_reversible_command (_("align selection (relative)"));
3646 /* move first one specially */
3648 r->clear_changes ();
3649 r->set_position (pos);
3650 _session->add_command(new StatefulDiffCommand (r));
3652 /* move rest by the same amount */
3656 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3658 boost::shared_ptr<Region> region ((*i)->region());
3660 region->clear_changes ();
3663 region->set_position (region->position() + distance);
3665 region->set_position (region->position() - distance);
3668 _session->add_command(new StatefulDiffCommand (region));
3672 commit_reversible_command ();
3676 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3678 begin_reversible_command (_("align region"));
3679 align_region_internal (region, point, position);
3680 commit_reversible_command ();
3684 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3686 region->clear_changes ();
3690 region->set_position (region->adjust_to_sync (position));
3694 if (position > region->length()) {
3695 region->set_position (position - region->length());
3700 region->set_position (position);
3704 _session->add_command(new StatefulDiffCommand (region));
3708 Editor::trim_region_front ()
3714 Editor::trim_region_back ()
3716 trim_region (false);
3720 Editor::trim_region (bool front)
3722 framepos_t where = get_preferred_edit_position();
3723 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3729 begin_reversible_command (front ? _("trim front") : _("trim back"));
3731 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3732 if (!(*i)->region()->locked()) {
3734 (*i)->region()->clear_changes ();
3737 (*i)->region()->trim_front (where);
3738 maybe_locate_with_edit_preroll ( where );
3740 (*i)->region()->trim_end (where);
3741 maybe_locate_with_edit_preroll ( where );
3744 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3748 commit_reversible_command ();
3751 /** Trim the end of the selected regions to the position of the edit cursor */
3753 Editor::trim_region_to_loop ()
3755 Location* loc = _session->locations()->auto_loop_location();
3759 trim_region_to_location (*loc, _("trim to loop"));
3763 Editor::trim_region_to_punch ()
3765 Location* loc = _session->locations()->auto_punch_location();
3769 trim_region_to_location (*loc, _("trim to punch"));
3773 Editor::trim_region_to_location (const Location& loc, const char* str)
3775 RegionSelection rs = get_regions_from_selection_and_entered ();
3776 bool in_command = false;
3778 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3779 RegionView* rv = (*x);
3781 /* require region to span proposed trim */
3782 switch (rv->region()->coverage (loc.start(), loc.end())) {
3783 case Evoral::OverlapInternal:
3789 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3798 if (tav->track() != 0) {
3799 speed = tav->track()->speed();
3802 start = session_frame_to_track_frame (loc.start(), speed);
3803 end = session_frame_to_track_frame (loc.end(), speed);
3805 rv->region()->clear_changes ();
3806 rv->region()->trim_to (start, (end - start));
3809 begin_reversible_command (str);
3812 _session->add_command(new StatefulDiffCommand (rv->region()));
3816 commit_reversible_command ();
3821 Editor::trim_region_to_previous_region_end ()
3823 return trim_to_region(false);
3827 Editor::trim_region_to_next_region_start ()
3829 return trim_to_region(true);
3833 Editor::trim_to_region(bool forward)
3835 RegionSelection rs = get_regions_from_selection_and_entered ();
3836 bool in_command = false;
3838 boost::shared_ptr<Region> next_region;
3840 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3842 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3848 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3856 if (atav->track() != 0) {
3857 speed = atav->track()->speed();
3861 boost::shared_ptr<Region> region = arv->region();
3862 boost::shared_ptr<Playlist> playlist (region->playlist());
3864 region->clear_changes ();
3868 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3874 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3875 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3879 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3885 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3887 arv->region_changed (ARDOUR::bounds_change);
3891 begin_reversible_command (_("trim to region"));
3894 _session->add_command(new StatefulDiffCommand (region));
3898 commit_reversible_command ();
3903 Editor::unfreeze_route ()
3905 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3909 clicked_routeview->track()->unfreeze ();
3913 Editor::_freeze_thread (void* arg)
3915 return static_cast<Editor*>(arg)->freeze_thread ();
3919 Editor::freeze_thread ()
3921 /* create event pool because we may need to talk to the session */
3922 SessionEvent::create_per_thread_pool ("freeze events", 64);
3923 /* create per-thread buffers for process() tree to use */
3924 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3925 current_interthread_info->done = true;
3930 Editor::freeze_route ()
3936 /* stop transport before we start. this is important */
3938 _session->request_transport_speed (0.0);
3940 /* wait for just a little while, because the above call is asynchronous */
3942 Glib::usleep (250000);
3944 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3948 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3950 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3951 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3953 d.set_title (_("Cannot freeze"));
3958 if (clicked_routeview->track()->has_external_redirects()) {
3959 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"
3960 "Freezing will only process the signal as far as the first send/insert/return."),
3961 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3963 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3964 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3965 d.set_title (_("Freeze Limits"));
3967 int response = d.run ();
3970 case Gtk::RESPONSE_CANCEL:
3977 InterThreadInfo itt;
3978 current_interthread_info = &itt;
3980 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3982 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3984 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3986 while (!itt.done && !itt.cancel) {
3987 gtk_main_iteration ();
3990 pthread_join (itt.thread, 0);
3991 current_interthread_info = 0;
3995 Editor::bounce_range_selection (bool replace, bool enable_processing)
3997 if (selection->time.empty()) {
4001 TrackSelection views = selection->tracks;
4003 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4005 if (enable_processing) {
4007 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4009 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
4011 _("You can't perform this operation because the processing of the signal "
4012 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
4013 "You can do this without processing, which is a different operation.")
4015 d.set_title (_("Cannot bounce"));
4022 framepos_t start = selection->time[clicked_selection].start;
4023 framepos_t end = selection->time[clicked_selection].end;
4024 framepos_t cnt = end - start + 1;
4025 bool in_command = false;
4027 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
4029 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
4035 boost::shared_ptr<Playlist> playlist;
4037 if ((playlist = rtv->playlist()) == 0) {
4041 InterThreadInfo itt;
4043 playlist->clear_changes ();
4044 playlist->clear_owned_changes ();
4046 boost::shared_ptr<Region> r;
4048 if (enable_processing) {
4049 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
4051 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4059 list<AudioRange> ranges;
4060 ranges.push_back (AudioRange (start, start+cnt, 0));
4061 playlist->cut (ranges); // discard result
4062 playlist->add_region (r, start);
4066 begin_reversible_command (_("bounce range"));
4069 vector<Command*> cmds;
4070 playlist->rdiff (cmds);
4071 _session->add_commands (cmds);
4073 _session->add_command (new StatefulDiffCommand (playlist));
4077 commit_reversible_command ();
4081 /** Delete selected regions, automation points or a time range */
4085 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4086 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4087 bool deleted = false;
4088 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4089 deleted = current_mixer_strip->delete_processors ();
4095 /** Cut selected regions, automation points or a time range */
4102 /** Copy selected regions, automation points or a time range */
4110 /** @return true if a Cut, Copy or Clear is possible */
4112 Editor::can_cut_copy () const
4114 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4121 /** Cut, copy or clear selected regions, automation points or a time range.
4122 * @param op Operation (Delete, Cut, Copy or Clear)
4125 Editor::cut_copy (CutCopyOp op)
4127 /* only cancel selection if cut/copy is successful.*/
4133 opname = _("delete");
4142 opname = _("clear");
4146 /* if we're deleting something, and the mouse is still pressed,
4147 the thing we started a drag for will be gone when we release
4148 the mouse button(s). avoid this. see part 2 at the end of
4152 if (op == Delete || op == Cut || op == Clear) {
4153 if (_drags->active ()) {
4158 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4159 cut_buffer->clear ();
4161 if (entered_marker) {
4163 /* cut/delete op while pointing at a marker */
4166 Location* loc = find_location_from_marker (entered_marker, ignored);
4168 if (_session && loc) {
4169 entered_marker = NULL;
4170 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4177 switch (mouse_mode) {
4180 begin_reversible_command (opname + ' ' + X_("MIDI"));
4182 commit_reversible_command ();
4188 bool did_edit = false;
4190 if (!selection->regions.empty() || !selection->points.empty()) {
4191 begin_reversible_command (opname + ' ' + _("objects"));
4194 if (!selection->regions.empty()) {
4195 cut_copy_regions (op, selection->regions);
4197 if (op == Cut || op == Delete) {
4198 selection->clear_regions ();
4202 if (!selection->points.empty()) {
4203 cut_copy_points (op);
4205 if (op == Cut || op == Delete) {
4206 selection->clear_points ();
4209 } else if (selection->time.empty()) {
4210 framepos_t start, end;
4211 /* no time selection, see if we can get an edit range
4214 if (get_edit_op_range (start, end)) {
4215 selection->set (start, end);
4217 } else if (!selection->time.empty()) {
4218 begin_reversible_command (opname + ' ' + _("range"));
4221 cut_copy_ranges (op);
4223 if (op == Cut || op == Delete) {
4224 selection->clear_time ();
4229 /* reset repeated paste state */
4232 commit_reversible_command ();
4235 if (op == Delete || op == Cut || op == Clear) {
4241 struct AutomationRecord {
4242 AutomationRecord () : state (0) , line(NULL) {}
4243 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4245 XMLNode* state; ///< state before any operation
4246 const AutomationLine* line; ///< line this came from
4247 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4249 struct PointsSelectionPositionSorter {
4250 bool operator() (ControlPoint* a, ControlPoint* b) {
4251 return (*(a->model()))->when < (*(b->model()))->when;
4254 /** Cut, copy or clear selected automation points.
4255 * @param op Operation (Cut, Copy or Clear)
4258 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4260 if (selection->points.empty ()) {
4264 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4265 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4267 /* Keep a record of the AutomationLists that we end up using in this operation */
4268 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4271 /* user could select points in any order */
4272 selection->points.sort(PointsSelectionPositionSorter ());
4274 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4275 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4276 const AutomationLine& line = (*sel_point)->line();
4277 const boost::shared_ptr<AutomationList> al = line.the_list();
4278 if (lists.find (al) == lists.end ()) {
4279 /* We haven't seen this list yet, so make a record for it. This includes
4280 taking a copy of its current state, in case this is needed for undo later.
4282 lists[al] = AutomationRecord (&al->get_state (), &line);
4286 if (op == Cut || op == Copy) {
4287 /* This operation will involve putting things in the cut buffer, so create an empty
4288 ControlList for each of our source lists to put the cut buffer data in.
4290 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4291 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4294 /* Add all selected points to the relevant copy ControlLists */
4295 framepos_t start = std::numeric_limits<framepos_t>::max();
4296 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4297 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4298 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4300 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4302 /* Update earliest MIDI start time in beats */
4303 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4305 /* Update earliest session start time in frames */
4306 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4310 /* Snap start time backwards, so copy/paste is snap aligned. */
4312 if (earliest == Evoral::Beats::max()) {
4313 earliest = Evoral::Beats(); // Weird... don't offset
4315 earliest.round_down_to_beat();
4317 if (start == std::numeric_limits<double>::max()) {
4318 start = 0; // Weird... don't offset
4320 snap_to(start, RoundDownMaybe);
4323 const double line_offset = midi ? earliest.to_double() : start;
4324 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4325 /* Correct this copy list so that it is relative to the earliest
4326 start time, so relative ordering between points is preserved
4327 when copying from several lists and the paste starts at the
4328 earliest copied piece of data. */
4329 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4330 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4331 (*ctrl_evt)->when -= line_offset;
4334 /* And add it to the cut buffer */
4335 cut_buffer->add (al_cpy);
4339 if (op == Delete || op == Cut) {
4340 /* This operation needs to remove things from the main AutomationList, so do that now */
4342 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4343 i->first->freeze ();
4346 /* Remove each selected point from its AutomationList */
4347 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4348 AutomationLine& line = (*sel_point)->line ();
4349 boost::shared_ptr<AutomationList> al = line.the_list();
4353 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4354 /* removing of first and last gain point in region gain lines is prohibited*/
4355 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4361 al->erase ((*sel_point)->model ());
4365 /* Thaw the lists and add undo records for them */
4366 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4367 boost::shared_ptr<AutomationList> al = i->first;
4369 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4374 /** Cut, copy or clear selected automation points.
4375 * @param op Operation (Cut, Copy or Clear)
4378 Editor::cut_copy_midi (CutCopyOp op)
4380 Evoral::Beats earliest = Evoral::Beats::max();
4381 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4382 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4384 if (!mrv->selection().empty()) {
4385 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4387 mrv->cut_copy_clear (op);
4389 /* XXX: not ideal, as there may be more than one track involved in the selection */
4390 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4394 if (!selection->points.empty()) {
4395 cut_copy_points (op, earliest, true);
4396 if (op == Cut || op == Delete) {
4397 selection->clear_points ();
4402 struct lt_playlist {
4403 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4404 return a.playlist < b.playlist;
4408 struct PlaylistMapping {
4410 boost::shared_ptr<Playlist> pl;
4412 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4415 /** Remove `clicked_regionview' */
4417 Editor::remove_clicked_region ()
4419 if (clicked_routeview == 0 || clicked_regionview == 0) {
4423 begin_reversible_command (_("remove region"));
4425 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4427 playlist->clear_changes ();
4428 playlist->clear_owned_changes ();
4429 playlist->remove_region (clicked_regionview->region());
4430 if (Config->get_edit_mode() == Ripple)
4431 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4433 /* We might have removed regions, which alters other regions' layering_index,
4434 so we need to do a recursive diff here.
4436 vector<Command*> cmds;
4437 playlist->rdiff (cmds);
4438 _session->add_commands (cmds);
4440 _session->add_command(new StatefulDiffCommand (playlist));
4441 commit_reversible_command ();
4445 /** Remove the selected regions */
4447 Editor::remove_selected_regions ()
4449 RegionSelection rs = get_regions_from_selection_and_entered ();
4451 if (!_session || rs.empty()) {
4455 list<boost::shared_ptr<Region> > regions_to_remove;
4457 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4458 // we can't just remove the region(s) in this loop because
4459 // this removes them from the RegionSelection, and they thus
4460 // disappear from underneath the iterator, and the ++i above
4461 // SEGVs in a puzzling fashion.
4463 // so, first iterate over the regions to be removed from rs and
4464 // add them to the regions_to_remove list, and then
4465 // iterate over the list to actually remove them.
4467 regions_to_remove.push_back ((*i)->region());
4470 vector<boost::shared_ptr<Playlist> > playlists;
4472 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4474 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4477 // is this check necessary?
4481 /* get_regions_from_selection_and_entered() guarantees that
4482 the playlists involved are unique, so there is no need
4486 playlists.push_back (playlist);
4488 playlist->clear_changes ();
4489 playlist->clear_owned_changes ();
4490 playlist->freeze ();
4491 playlist->remove_region (*rl);
4492 if (Config->get_edit_mode() == Ripple)
4493 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4497 vector<boost::shared_ptr<Playlist> >::iterator pl;
4498 bool in_command = false;
4500 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4503 /* We might have removed regions, which alters other regions' layering_index,
4504 so we need to do a recursive diff here.
4508 begin_reversible_command (_("remove region"));
4511 vector<Command*> cmds;
4512 (*pl)->rdiff (cmds);
4513 _session->add_commands (cmds);
4515 _session->add_command(new StatefulDiffCommand (*pl));
4519 commit_reversible_command ();
4523 /** Cut, copy or clear selected regions.
4524 * @param op Operation (Cut, Copy or Clear)
4527 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4529 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4530 a map when we want ordered access to both elements. i think.
4533 vector<PlaylistMapping> pmap;
4535 framepos_t first_position = max_framepos;
4537 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4538 FreezeList freezelist;
4540 /* get ordering correct before we cut/copy */
4542 rs.sort_by_position_and_track ();
4544 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4546 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4548 if (op == Cut || op == Clear || op == Delete) {
4549 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4552 FreezeList::iterator fl;
4554 // only take state if this is a new playlist.
4555 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4561 if (fl == freezelist.end()) {
4562 pl->clear_changes();
4563 pl->clear_owned_changes ();
4565 freezelist.insert (pl);
4570 TimeAxisView* tv = &(*x)->get_time_axis_view();
4571 vector<PlaylistMapping>::iterator z;
4573 for (z = pmap.begin(); z != pmap.end(); ++z) {
4574 if ((*z).tv == tv) {
4579 if (z == pmap.end()) {
4580 pmap.push_back (PlaylistMapping (tv));
4584 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4586 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4589 /* region not yet associated with a playlist (e.g. unfinished
4596 TimeAxisView& tv = (*x)->get_time_axis_view();
4597 boost::shared_ptr<Playlist> npl;
4598 RegionSelection::iterator tmp;
4605 vector<PlaylistMapping>::iterator z;
4607 for (z = pmap.begin(); z != pmap.end(); ++z) {
4608 if ((*z).tv == &tv) {
4613 assert (z != pmap.end());
4616 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4624 boost::shared_ptr<Region> r = (*x)->region();
4625 boost::shared_ptr<Region> _xx;
4631 pl->remove_region (r);
4632 if (Config->get_edit_mode() == Ripple)
4633 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4637 _xx = RegionFactory::create (r);
4638 npl->add_region (_xx, r->position() - first_position);
4639 pl->remove_region (r);
4640 if (Config->get_edit_mode() == Ripple)
4641 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4645 /* copy region before adding, so we're not putting same object into two different playlists */
4646 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4650 pl->remove_region (r);
4651 if (Config->get_edit_mode() == Ripple)
4652 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4661 list<boost::shared_ptr<Playlist> > foo;
4663 /* the pmap is in the same order as the tracks in which selected regions occurred */
4665 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4668 foo.push_back ((*i).pl);
4673 cut_buffer->set (foo);
4677 _last_cut_copy_source_track = 0;
4679 _last_cut_copy_source_track = pmap.front().tv;
4683 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4686 /* We might have removed regions, which alters other regions' layering_index,
4687 so we need to do a recursive diff here.
4689 vector<Command*> cmds;
4690 (*pl)->rdiff (cmds);
4691 _session->add_commands (cmds);
4693 _session->add_command (new StatefulDiffCommand (*pl));
4698 Editor::cut_copy_ranges (CutCopyOp op)
4700 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4702 /* Sort the track selection now, so that it if is used, the playlists
4703 selected by the calls below to cut_copy_clear are in the order that
4704 their tracks appear in the editor. This makes things like paste
4705 of ranges work properly.
4708 sort_track_selection (ts);
4711 if (!entered_track) {
4714 ts.push_back (entered_track);
4717 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4718 (*i)->cut_copy_clear (*selection, op);
4723 Editor::paste (float times, bool from_context)
4725 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4727 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4731 Editor::mouse_paste ()
4736 if (!mouse_frame (where, ignored)) {
4741 paste_internal (where, 1, get_grid_music_divisions (0));
4745 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4747 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4749 if (cut_buffer->empty(internal_editing())) {
4753 if (position == max_framepos) {
4754 position = get_preferred_edit_position();
4755 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4758 if (position == last_paste_pos) {
4759 /* repeated paste in the same position */
4762 /* paste in new location, reset repeated paste state */
4764 last_paste_pos = position;
4767 /* get everything in the correct order */
4770 if (!selection->tracks.empty()) {
4771 /* If there is a track selection, paste into exactly those tracks and
4772 only those tracks. This allows the user to be explicit and override
4773 the below "do the reasonable thing" logic. */
4774 ts = selection->tracks.filter_to_unique_playlists ();
4775 sort_track_selection (ts);
4777 /* Figure out which track to base the paste at. */
4778 TimeAxisView* base_track = NULL;
4779 if (_edit_point == Editing::EditAtMouse && entered_track) {
4780 /* With the mouse edit point, paste onto the track under the mouse. */
4781 base_track = entered_track;
4782 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4783 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4784 base_track = &entered_regionview->get_time_axis_view();
4785 } else if (_last_cut_copy_source_track) {
4786 /* Paste to the track that the cut/copy came from (see mantis #333). */
4787 base_track = _last_cut_copy_source_track;
4789 /* This is "impossible" since we've copied... well, do nothing. */
4793 /* Walk up to parent if necessary, so base track is a route. */
4794 while (base_track->get_parent()) {
4795 base_track = base_track->get_parent();
4798 /* Add base track and all tracks below it. The paste logic will select
4799 the appropriate object types from the cut buffer in relative order. */
4800 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4801 if ((*i)->order() >= base_track->order()) {
4806 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4807 sort_track_selection (ts);
4809 /* Add automation children of each track in order, for pasting several lines. */
4810 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4811 /* Add any automation children for pasting several lines */
4812 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4817 typedef RouteTimeAxisView::AutomationTracks ATracks;
4818 const ATracks& atracks = rtv->automation_tracks();
4819 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4820 i = ts.insert(i, a->second.get());
4825 /* We now have a list of trackviews starting at base_track, including
4826 automation children, in the order shown in the editor, e.g. R1,
4827 R1.A1, R1.A2, R2, R2.A1, ... */
4830 begin_reversible_command (Operations::paste);
4832 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4833 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4834 /* Only one line copied, and one automation track selected. Do a
4835 "greedy" paste from one automation type to another. */
4837 PasteContext ctx(paste_count, times, ItemCounts(), true);
4838 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4842 /* Paste into tracks */
4844 PasteContext ctx(paste_count, times, ItemCounts(), false);
4845 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4846 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4850 commit_reversible_command ();
4854 Editor::duplicate_regions (float times)
4856 RegionSelection rs (get_regions_from_selection_and_entered());
4857 duplicate_some_regions (rs, times);
4861 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4863 if (regions.empty ()) {
4867 boost::shared_ptr<Playlist> playlist;
4868 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4869 RegionSelection foo;
4871 framepos_t const start_frame = regions.start ();
4872 framepos_t const end_frame = regions.end_frame ();
4873 framecnt_t const gap = end_frame - start_frame + 1;
4875 begin_reversible_command (Operations::duplicate_region);
4877 selection->clear_regions ();
4879 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4881 boost::shared_ptr<Region> r ((*i)->region());
4883 TimeAxisView& tv = (*i)->get_time_axis_view();
4884 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4885 latest_regionviews.clear ();
4886 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4888 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4889 playlist = (*i)->region()->playlist();
4890 playlist->clear_changes ();
4891 playlist->duplicate (r, position, gap, times);
4892 _session->add_command(new StatefulDiffCommand (playlist));
4896 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4900 selection->set (foo);
4903 commit_reversible_command ();
4907 Editor::duplicate_selection (float times)
4909 if (selection->time.empty() || selection->tracks.empty()) {
4913 boost::shared_ptr<Playlist> playlist;
4915 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4917 bool in_command = false;
4919 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4920 if ((playlist = (*i)->playlist()) == 0) {
4923 playlist->clear_changes ();
4925 if (clicked_selection) {
4926 playlist->duplicate_range (selection->time[clicked_selection], times);
4928 playlist->duplicate_ranges (selection->time, times);
4932 begin_reversible_command (_("duplicate range selection"));
4935 _session->add_command (new StatefulDiffCommand (playlist));
4940 if (times == 1.0f) {
4941 // now "move" range selection to after the current range selection
4942 framecnt_t distance = 0;
4944 if (clicked_selection) {
4946 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4948 distance = selection->time.end_frame () - selection->time.start ();
4951 selection->move_time (distance);
4953 commit_reversible_command ();
4957 /** Reset all selected points to the relevant default value */
4959 Editor::reset_point_selection ()
4961 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4962 ARDOUR::AutomationList::iterator j = (*i)->model ();
4963 (*j)->value = (*i)->line().the_list()->default_value ();
4968 Editor::center_playhead ()
4970 float const page = _visible_canvas_width * samples_per_pixel;
4971 center_screen_internal (playhead_cursor->current_frame (), page);
4975 Editor::center_edit_point ()
4977 float const page = _visible_canvas_width * samples_per_pixel;
4978 center_screen_internal (get_preferred_edit_position(), page);
4981 /** Caller must begin and commit a reversible command */
4983 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4985 playlist->clear_changes ();
4987 _session->add_command (new StatefulDiffCommand (playlist));
4991 Editor::nudge_track (bool use_edit, bool forwards)
4993 boost::shared_ptr<Playlist> playlist;
4994 framepos_t distance;
4995 framepos_t next_distance;
4999 start = get_preferred_edit_position();
5004 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
5008 if (selection->tracks.empty()) {
5012 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5013 bool in_command = false;
5015 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5017 if ((playlist = (*i)->playlist()) == 0) {
5021 playlist->clear_changes ();
5022 playlist->clear_owned_changes ();
5024 playlist->nudge_after (start, distance, forwards);
5027 begin_reversible_command (_("nudge track"));
5030 vector<Command*> cmds;
5032 playlist->rdiff (cmds);
5033 _session->add_commands (cmds);
5035 _session->add_command (new StatefulDiffCommand (playlist));
5039 commit_reversible_command ();
5044 Editor::remove_last_capture ()
5046 vector<string> choices;
5053 if (Config->get_verify_remove_last_capture()) {
5054 prompt = _("Do you really want to destroy the last capture?"
5055 "\n(This is destructive and cannot be undone)");
5057 choices.push_back (_("No, do nothing."));
5058 choices.push_back (_("Yes, destroy it."));
5060 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5062 if (prompter.run () == 1) {
5063 _session->remove_last_capture ();
5064 _regions->redisplay ();
5068 _session->remove_last_capture();
5069 _regions->redisplay ();
5074 Editor::normalize_region ()
5080 RegionSelection rs = get_regions_from_selection_and_entered ();
5086 NormalizeDialog dialog (rs.size() > 1);
5088 if (dialog.run () != RESPONSE_ACCEPT) {
5092 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5095 /* XXX: should really only count audio regions here */
5096 int const regions = rs.size ();
5098 /* Make a list of the selected audio regions' maximum amplitudes, and also
5099 obtain the maximum amplitude of them all.
5101 list<double> max_amps;
5102 list<double> rms_vals;
5105 bool use_rms = dialog.constrain_rms ();
5107 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5108 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5112 dialog.descend (1.0 / regions);
5113 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5115 double r = arv->audio_region()->rms (&dialog);
5116 max_rms = max (max_rms, r);
5117 rms_vals.push_back (r);
5121 /* the user cancelled the operation */
5125 max_amps.push_back (a);
5126 max_amp = max (max_amp, a);
5130 list<double>::const_iterator a = max_amps.begin ();
5131 list<double>::const_iterator l = rms_vals.begin ();
5132 bool in_command = false;
5134 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5135 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5140 arv->region()->clear_changes ();
5142 double amp = dialog.normalize_individually() ? *a : max_amp;
5143 double target = dialog.target_peak (); // dB
5146 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5147 const double t_rms = dialog.target_rms ();
5148 const gain_t c_peak = dB_to_coefficient (target);
5149 const gain_t c_rms = dB_to_coefficient (t_rms);
5150 if ((amp_rms / c_rms) > (amp / c_peak)) {
5156 arv->audio_region()->normalize (amp, target);
5159 begin_reversible_command (_("normalize"));
5162 _session->add_command (new StatefulDiffCommand (arv->region()));
5169 commit_reversible_command ();
5175 Editor::reset_region_scale_amplitude ()
5181 RegionSelection rs = get_regions_from_selection_and_entered ();
5187 bool in_command = false;
5189 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5190 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5193 arv->region()->clear_changes ();
5194 arv->audio_region()->set_scale_amplitude (1.0f);
5197 begin_reversible_command ("reset gain");
5200 _session->add_command (new StatefulDiffCommand (arv->region()));
5204 commit_reversible_command ();
5209 Editor::adjust_region_gain (bool up)
5211 RegionSelection rs = get_regions_from_selection_and_entered ();
5213 if (!_session || rs.empty()) {
5217 bool in_command = false;
5219 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5220 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5225 arv->region()->clear_changes ();
5227 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5235 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5238 begin_reversible_command ("adjust region gain");
5241 _session->add_command (new StatefulDiffCommand (arv->region()));
5245 commit_reversible_command ();
5251 Editor::reverse_region ()
5257 Reverse rev (*_session);
5258 apply_filter (rev, _("reverse regions"));
5262 Editor::strip_region_silence ()
5268 RegionSelection rs = get_regions_from_selection_and_entered ();
5274 std::list<RegionView*> audio_only;
5276 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5277 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5279 audio_only.push_back (arv);
5283 assert (!audio_only.empty());
5285 StripSilenceDialog d (_session, audio_only);
5286 int const r = d.run ();
5290 if (r == Gtk::RESPONSE_OK) {
5291 ARDOUR::AudioIntervalMap silences;
5292 d.silences (silences);
5293 StripSilence s (*_session, silences, d.fade_length());
5295 apply_filter (s, _("strip silence"), &d);
5300 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5302 Evoral::Sequence<Evoral::Beats>::Notes selected;
5303 mrv.selection_as_notelist (selected, true);
5305 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5306 v.push_back (selected);
5308 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5310 return op (mrv.midi_region()->model(), pos_beats, v);
5314 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5320 bool in_command = false;
5322 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5323 RegionSelection::const_iterator tmp = r;
5326 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5329 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5332 begin_reversible_command (op.name ());
5336 _session->add_command (cmd);
5344 commit_reversible_command ();
5349 Editor::fork_region ()
5351 RegionSelection rs = get_regions_from_selection_and_entered ();
5357 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5358 bool in_command = false;
5362 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5363 RegionSelection::iterator tmp = r;
5366 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5370 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5371 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5372 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5375 begin_reversible_command (_("Fork Region(s)"));
5378 playlist->clear_changes ();
5379 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5380 _session->add_command(new StatefulDiffCommand (playlist));
5382 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5390 commit_reversible_command ();
5395 Editor::quantize_region ()
5398 quantize_regions(get_regions_from_selection_and_entered ());
5403 Editor::quantize_regions (const RegionSelection& rs)
5405 if (rs.n_midi_regions() == 0) {
5409 if (!quantize_dialog) {
5410 quantize_dialog = new QuantizeDialog (*this);
5413 if (quantize_dialog->is_mapped()) {
5414 /* in progress already */
5418 quantize_dialog->present ();
5419 const int r = quantize_dialog->run ();
5420 quantize_dialog->hide ();
5422 if (r == Gtk::RESPONSE_OK) {
5423 Quantize quant (quantize_dialog->snap_start(),
5424 quantize_dialog->snap_end(),
5425 quantize_dialog->start_grid_size(),
5426 quantize_dialog->end_grid_size(),
5427 quantize_dialog->strength(),
5428 quantize_dialog->swing(),
5429 quantize_dialog->threshold());
5431 apply_midi_note_edit_op (quant, rs);
5436 Editor::legatize_region (bool shrink_only)
5439 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5444 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5446 if (rs.n_midi_regions() == 0) {
5450 Legatize legatize(shrink_only);
5451 apply_midi_note_edit_op (legatize, rs);
5455 Editor::transform_region ()
5458 transform_regions(get_regions_from_selection_and_entered ());
5463 Editor::transform_regions (const RegionSelection& rs)
5465 if (rs.n_midi_regions() == 0) {
5472 const int r = td.run();
5475 if (r == Gtk::RESPONSE_OK) {
5476 Transform transform(td.get());
5477 apply_midi_note_edit_op(transform, rs);
5482 Editor::transpose_region ()
5485 transpose_regions(get_regions_from_selection_and_entered ());
5490 Editor::transpose_regions (const RegionSelection& rs)
5492 if (rs.n_midi_regions() == 0) {
5497 int const r = d.run ();
5499 if (r == RESPONSE_ACCEPT) {
5500 Transpose transpose(d.semitones ());
5501 apply_midi_note_edit_op (transpose, rs);
5506 Editor::insert_patch_change (bool from_context)
5508 RegionSelection rs = get_regions_from_selection_and_entered ();
5514 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5516 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5517 there may be more than one, but the PatchChangeDialog can only offer
5518 one set of patch menus.
5520 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5522 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5523 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5525 if (d.run() == RESPONSE_CANCEL) {
5529 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5530 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5532 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5533 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5540 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5542 RegionSelection rs = get_regions_from_selection_and_entered ();
5548 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5549 bool in_command = false;
5554 int const N = rs.size ();
5556 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5557 RegionSelection::iterator tmp = r;
5560 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5562 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5565 progress->descend (1.0 / N);
5568 if (arv->audio_region()->apply (filter, progress) == 0) {
5570 playlist->clear_changes ();
5571 playlist->clear_owned_changes ();
5574 begin_reversible_command (command);
5578 if (filter.results.empty ()) {
5580 /* no regions returned; remove the old one */
5581 playlist->remove_region (arv->region ());
5585 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5587 /* first region replaces the old one */
5588 playlist->replace_region (arv->region(), *res, (*res)->position());
5592 while (res != filter.results.end()) {
5593 playlist->add_region (*res, (*res)->position());
5599 /* We might have removed regions, which alters other regions' layering_index,
5600 so we need to do a recursive diff here.
5602 vector<Command*> cmds;
5603 playlist->rdiff (cmds);
5604 _session->add_commands (cmds);
5606 _session->add_command(new StatefulDiffCommand (playlist));
5610 progress->ascend ();
5619 commit_reversible_command ();
5624 Editor::external_edit_region ()
5630 Editor::reset_region_gain_envelopes ()
5632 RegionSelection rs = get_regions_from_selection_and_entered ();
5634 if (!_session || rs.empty()) {
5638 bool in_command = false;
5640 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5641 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5643 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5644 XMLNode& before (alist->get_state());
5646 arv->audio_region()->set_default_envelope ();
5649 begin_reversible_command (_("reset region gain"));
5652 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5657 commit_reversible_command ();
5662 Editor::set_region_gain_visibility (RegionView* rv)
5664 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5666 arv->update_envelope_visibility();
5671 Editor::set_gain_envelope_visibility ()
5677 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5678 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5680 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5686 Editor::toggle_gain_envelope_active ()
5688 if (_ignore_region_action) {
5692 RegionSelection rs = get_regions_from_selection_and_entered ();
5694 if (!_session || rs.empty()) {
5698 bool in_command = false;
5700 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5701 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5703 arv->region()->clear_changes ();
5704 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5707 begin_reversible_command (_("region gain envelope active"));
5710 _session->add_command (new StatefulDiffCommand (arv->region()));
5715 commit_reversible_command ();
5720 Editor::toggle_region_lock ()
5722 if (_ignore_region_action) {
5726 RegionSelection rs = get_regions_from_selection_and_entered ();
5728 if (!_session || rs.empty()) {
5732 begin_reversible_command (_("toggle region lock"));
5734 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5735 (*i)->region()->clear_changes ();
5736 (*i)->region()->set_locked (!(*i)->region()->locked());
5737 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5740 commit_reversible_command ();
5744 Editor::toggle_region_video_lock ()
5746 if (_ignore_region_action) {
5750 RegionSelection rs = get_regions_from_selection_and_entered ();
5752 if (!_session || rs.empty()) {
5756 begin_reversible_command (_("Toggle Video Lock"));
5758 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5759 (*i)->region()->clear_changes ();
5760 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5761 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5764 commit_reversible_command ();
5768 Editor::toggle_region_lock_style ()
5770 if (_ignore_region_action) {
5774 RegionSelection rs = get_regions_from_selection_and_entered ();
5776 if (!_session || rs.empty()) {
5780 begin_reversible_command (_("region lock style"));
5782 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5783 (*i)->region()->clear_changes ();
5784 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5785 (*i)->region()->set_position_lock_style (ns);
5786 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5789 commit_reversible_command ();
5793 Editor::toggle_opaque_region ()
5795 if (_ignore_region_action) {
5799 RegionSelection rs = get_regions_from_selection_and_entered ();
5801 if (!_session || rs.empty()) {
5805 begin_reversible_command (_("change region opacity"));
5807 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5808 (*i)->region()->clear_changes ();
5809 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5810 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5813 commit_reversible_command ();
5817 Editor::toggle_record_enable ()
5819 bool new_state = false;
5821 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5822 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5825 if (!rtav->is_track())
5829 new_state = !rtav->track()->rec_enable_control()->get_value();
5833 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5838 Editor::toggle_solo ()
5840 bool new_state = false;
5842 boost::shared_ptr<ControlList> cl (new ControlList);
5844 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5845 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5852 new_state = !rtav->route()->soloed ();
5856 cl->push_back (rtav->route()->solo_control());
5859 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5863 Editor::toggle_mute ()
5865 bool new_state = false;
5867 boost::shared_ptr<RouteList> rl (new RouteList);
5869 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5870 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5877 new_state = !rtav->route()->muted();
5881 rl->push_back (rtav->route());
5884 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5888 Editor::toggle_solo_isolate ()
5894 Editor::fade_range ()
5896 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5898 begin_reversible_command (_("fade range"));
5900 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5901 (*i)->fade_range (selection->time);
5904 commit_reversible_command ();
5909 Editor::set_fade_length (bool in)
5911 RegionSelection rs = get_regions_from_selection_and_entered ();
5917 /* we need a region to measure the offset from the start */
5919 RegionView* rv = rs.front ();
5921 framepos_t pos = get_preferred_edit_position();
5925 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5926 /* edit point is outside the relevant region */
5931 if (pos <= rv->region()->position()) {
5935 len = pos - rv->region()->position();
5936 cmd = _("set fade in length");
5938 if (pos >= rv->region()->last_frame()) {
5942 len = rv->region()->last_frame() - pos;
5943 cmd = _("set fade out length");
5946 bool in_command = false;
5948 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5949 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5955 boost::shared_ptr<AutomationList> alist;
5957 alist = tmp->audio_region()->fade_in();
5959 alist = tmp->audio_region()->fade_out();
5962 XMLNode &before = alist->get_state();
5965 tmp->audio_region()->set_fade_in_length (len);
5966 tmp->audio_region()->set_fade_in_active (true);
5968 tmp->audio_region()->set_fade_out_length (len);
5969 tmp->audio_region()->set_fade_out_active (true);
5973 begin_reversible_command (cmd);
5976 XMLNode &after = alist->get_state();
5977 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5981 commit_reversible_command ();
5986 Editor::set_fade_in_shape (FadeShape shape)
5988 RegionSelection rs = get_regions_from_selection_and_entered ();
5993 bool in_command = false;
5995 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5996 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6002 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
6003 XMLNode &before = alist->get_state();
6005 tmp->audio_region()->set_fade_in_shape (shape);
6008 begin_reversible_command (_("set fade in shape"));
6011 XMLNode &after = alist->get_state();
6012 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6016 commit_reversible_command ();
6021 Editor::set_fade_out_shape (FadeShape shape)
6023 RegionSelection rs = get_regions_from_selection_and_entered ();
6028 bool in_command = false;
6030 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6031 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6037 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
6038 XMLNode &before = alist->get_state();
6040 tmp->audio_region()->set_fade_out_shape (shape);
6043 begin_reversible_command (_("set fade out shape"));
6046 XMLNode &after = alist->get_state();
6047 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
6051 commit_reversible_command ();
6056 Editor::set_fade_in_active (bool yn)
6058 RegionSelection rs = get_regions_from_selection_and_entered ();
6063 bool in_command = false;
6065 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6066 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6073 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6075 ar->clear_changes ();
6076 ar->set_fade_in_active (yn);
6079 begin_reversible_command (_("set fade in active"));
6082 _session->add_command (new StatefulDiffCommand (ar));
6086 commit_reversible_command ();
6091 Editor::set_fade_out_active (bool yn)
6093 RegionSelection rs = get_regions_from_selection_and_entered ();
6098 bool in_command = false;
6100 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6101 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6107 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6109 ar->clear_changes ();
6110 ar->set_fade_out_active (yn);
6113 begin_reversible_command (_("set fade out active"));
6116 _session->add_command(new StatefulDiffCommand (ar));
6120 commit_reversible_command ();
6125 Editor::toggle_region_fades (int dir)
6127 if (_ignore_region_action) {
6131 boost::shared_ptr<AudioRegion> ar;
6134 RegionSelection rs = get_regions_from_selection_and_entered ();
6140 RegionSelection::iterator i;
6141 for (i = rs.begin(); i != rs.end(); ++i) {
6142 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6144 yn = ar->fade_out_active ();
6146 yn = ar->fade_in_active ();
6152 if (i == rs.end()) {
6156 /* XXX should this undo-able? */
6157 bool in_command = false;
6159 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6160 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6163 ar->clear_changes ();
6165 if (dir == 1 || dir == 0) {
6166 ar->set_fade_in_active (!yn);
6169 if (dir == -1 || dir == 0) {
6170 ar->set_fade_out_active (!yn);
6173 begin_reversible_command (_("toggle fade active"));
6176 _session->add_command(new StatefulDiffCommand (ar));
6180 commit_reversible_command ();
6185 /** Update region fade visibility after its configuration has been changed */
6187 Editor::update_region_fade_visibility ()
6189 bool _fade_visibility = _session->config.get_show_region_fades ();
6191 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6192 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6194 if (_fade_visibility) {
6195 v->audio_view()->show_all_fades ();
6197 v->audio_view()->hide_all_fades ();
6204 Editor::set_edit_point ()
6209 if (!mouse_frame (where, ignored)) {
6215 if (selection->markers.empty()) {
6217 mouse_add_new_marker (where);
6222 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6225 loc->move_to (where);
6231 Editor::set_playhead_cursor ()
6233 if (entered_marker) {
6234 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6239 if (!mouse_frame (where, ignored)) {
6246 _session->request_locate (where, _session->transport_rolling());
6250 if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6251 cancel_time_selection();
6256 Editor::split_region ()
6258 if (_drags->active ()) {
6262 //if a range is selected, separate it
6263 if ( !selection->time.empty()) {
6264 separate_regions_between (selection->time);
6268 //if no range was selected, try to find some regions to split
6269 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6271 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6273 framepos_t where = get_preferred_edit_position ();
6279 if (snap_musical()) {
6280 split_regions_at (where, rs, get_grid_music_divisions (0));
6282 split_regions_at (where, rs, 0);
6288 Editor::select_next_route()
6290 if (selection->tracks.empty()) {
6291 selection->set (track_views.front());
6295 TimeAxisView* current = selection->tracks.front();
6299 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6301 if (*i == current) {
6303 if (i != track_views.end()) {
6306 current = (*(track_views.begin()));
6307 //selection->set (*(track_views.begin()));
6313 rui = dynamic_cast<RouteUI *>(current);
6315 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6317 selection->set (current);
6319 ensure_time_axis_view_is_visible (*current, false);
6323 Editor::select_prev_route()
6325 if (selection->tracks.empty()) {
6326 selection->set (track_views.front());
6330 TimeAxisView* current = selection->tracks.front();
6334 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6336 if (*i == current) {
6338 if (i != track_views.rend()) {
6341 current = *(track_views.rbegin());
6346 rui = dynamic_cast<RouteUI *>(current);
6348 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6350 selection->set (current);
6352 ensure_time_axis_view_is_visible (*current, false);
6356 Editor::set_loop_from_selection (bool play)
6358 if (_session == 0) {
6362 framepos_t start, end;
6363 if (!get_selection_extents ( start, end))
6366 set_loop_range (start, end, _("set loop range from selection"));
6369 _session->request_play_loop (true, true);
6374 Editor::set_loop_from_region (bool play)
6376 framepos_t start, end;
6377 if (!get_selection_extents ( start, end))
6380 set_loop_range (start, end, _("set loop range from region"));
6383 _session->request_locate (start, true);
6384 _session->request_play_loop (true);
6389 Editor::set_punch_from_selection ()
6391 if (_session == 0) {
6395 framepos_t start, end;
6396 if (!get_selection_extents ( start, end))
6399 set_punch_range (start, end, _("set punch range from selection"));
6403 Editor::set_auto_punch_range ()
6405 // auto punch in/out button from a single button
6406 // If Punch In is unset, set punch range from playhead to end, enable punch in
6407 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6408 // rewound beyond the Punch In marker, in which case that marker will be moved back
6409 // to the current playhead position.
6410 // If punch out is set, it clears the punch range and Punch In/Out buttons
6412 if (_session == 0) {
6416 Location* tpl = transport_punch_location();
6417 framepos_t now = playhead_cursor->current_frame();
6418 framepos_t begin = now;
6419 framepos_t end = _session->current_end_frame();
6421 if (!_session->config.get_punch_in()) {
6422 // First Press - set punch in and create range from here to eternity
6423 set_punch_range (begin, end, _("Auto Punch In"));
6424 _session->config.set_punch_in(true);
6425 } else if (tpl && !_session->config.get_punch_out()) {
6426 // Second press - update end range marker and set punch_out
6427 if (now < tpl->start()) {
6428 // playhead has been rewound - move start back and pretend nothing happened
6430 set_punch_range (begin, end, _("Auto Punch In/Out"));
6432 // normal case for 2nd press - set the punch out
6433 end = playhead_cursor->current_frame ();
6434 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6435 _session->config.set_punch_out(true);
6438 if (_session->config.get_punch_out()) {
6439 _session->config.set_punch_out(false);
6442 if (_session->config.get_punch_in()) {
6443 _session->config.set_punch_in(false);
6448 // third press - unset punch in/out and remove range
6449 _session->locations()->remove(tpl);
6456 Editor::set_session_extents_from_selection ()
6458 if (_session == 0) {
6462 framepos_t start, end;
6463 if (!get_selection_extents ( start, end))
6467 if ((loc = _session->locations()->session_range_location()) == 0) {
6468 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6470 XMLNode &before = loc->get_state();
6472 _session->set_session_extents (start, end);
6474 XMLNode &after = loc->get_state();
6476 begin_reversible_command (_("set session start/end from selection"));
6478 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6480 commit_reversible_command ();
6483 _session->set_end_is_free (false);
6487 Editor::set_punch_start_from_edit_point ()
6491 framepos_t start = 0;
6492 framepos_t end = max_framepos;
6494 //use the existing punch end, if any
6495 Location* tpl = transport_punch_location();
6500 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6501 start = _session->audible_frame();
6503 start = get_preferred_edit_position();
6506 //snap the selection start/end
6509 //if there's not already a sensible selection endpoint, go "forever"
6510 if ( start > end ) {
6514 set_punch_range (start, end, _("set punch start from EP"));
6520 Editor::set_punch_end_from_edit_point ()
6524 framepos_t start = 0;
6525 framepos_t end = max_framepos;
6527 //use the existing punch start, if any
6528 Location* tpl = transport_punch_location();
6530 start = tpl->start();
6533 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6534 end = _session->audible_frame();
6536 end = get_preferred_edit_position();
6539 //snap the selection start/end
6542 set_punch_range (start, end, _("set punch end from EP"));
6548 Editor::set_loop_start_from_edit_point ()
6552 framepos_t start = 0;
6553 framepos_t end = max_framepos;
6555 //use the existing loop end, if any
6556 Location* tpl = transport_loop_location();
6561 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6562 start = _session->audible_frame();
6564 start = get_preferred_edit_position();
6567 //snap the selection start/end
6570 //if there's not already a sensible selection endpoint, go "forever"
6571 if ( start > end ) {
6575 set_loop_range (start, end, _("set loop start from EP"));
6581 Editor::set_loop_end_from_edit_point ()
6585 framepos_t start = 0;
6586 framepos_t end = max_framepos;
6588 //use the existing loop start, if any
6589 Location* tpl = transport_loop_location();
6591 start = tpl->start();
6594 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6595 end = _session->audible_frame();
6597 end = get_preferred_edit_position();
6600 //snap the selection start/end
6603 set_loop_range (start, end, _("set loop end from EP"));
6608 Editor::set_punch_from_region ()
6610 framepos_t start, end;
6611 if (!get_selection_extents ( start, end))
6614 set_punch_range (start, end, _("set punch range from region"));
6618 Editor::pitch_shift_region ()
6620 RegionSelection rs = get_regions_from_selection_and_entered ();
6622 RegionSelection audio_rs;
6623 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6624 if (dynamic_cast<AudioRegionView*> (*i)) {
6625 audio_rs.push_back (*i);
6629 if (audio_rs.empty()) {
6633 pitch_shift (audio_rs, 1.2);
6637 Editor::set_tempo_from_region ()
6639 RegionSelection rs = get_regions_from_selection_and_entered ();
6641 if (!_session || rs.empty()) {
6645 RegionView* rv = rs.front();
6647 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6651 Editor::use_range_as_bar ()
6653 framepos_t start, end;
6654 if (get_edit_op_range (start, end)) {
6655 define_one_bar (start, end);
6660 Editor::define_one_bar (framepos_t start, framepos_t end)
6662 framepos_t length = end - start;
6664 const Meter& m (_session->tempo_map().meter_at_frame (start));
6666 /* length = 1 bar */
6668 /* We're going to deliver a constant tempo here,
6669 so we can use frames per beat to determine length.
6670 now we want frames per beat.
6671 we have frames per bar, and beats per bar, so ...
6674 /* XXXX METER MATH */
6676 double frames_per_beat = length / m.divisions_per_bar();
6678 /* beats per minute = */
6680 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6682 /* now decide whether to:
6684 (a) set global tempo
6685 (b) add a new tempo marker
6689 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6691 bool do_global = false;
6693 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6695 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6696 at the start, or create a new marker
6699 vector<string> options;
6700 options.push_back (_("Cancel"));
6701 options.push_back (_("Add new marker"));
6702 options.push_back (_("Set global tempo"));
6705 _("Define one bar"),
6706 _("Do you want to set the global tempo or add a new tempo marker?"),
6710 c.set_default_response (2);
6726 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6727 if the marker is at the region starter, change it, otherwise add
6732 begin_reversible_command (_("set tempo from region"));
6733 XMLNode& before (_session->tempo_map().get_state());
6736 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6737 } else if (t.frame() == start) {
6738 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6740 const Tempo tempo (beats_per_minute, t.note_type());
6741 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6744 XMLNode& after (_session->tempo_map().get_state());
6746 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6747 commit_reversible_command ();
6751 Editor::split_region_at_transients ()
6753 AnalysisFeatureList positions;
6755 RegionSelection rs = get_regions_from_selection_and_entered ();
6757 if (!_session || rs.empty()) {
6761 begin_reversible_command (_("split regions"));
6763 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6765 RegionSelection::iterator tmp;
6770 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6773 ar->transients (positions);
6774 split_region_at_points ((*i)->region(), positions, true);
6781 commit_reversible_command ();
6786 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6788 bool use_rhythmic_rodent = false;
6790 boost::shared_ptr<Playlist> pl = r->playlist();
6792 list<boost::shared_ptr<Region> > new_regions;
6798 if (positions.empty()) {
6802 if (positions.size() > 20 && can_ferret) {
6803 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);
6804 MessageDialog msg (msgstr,
6807 Gtk::BUTTONS_OK_CANCEL);
6810 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6811 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6813 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6816 msg.set_title (_("Excessive split?"));
6819 int response = msg.run();
6825 case RESPONSE_APPLY:
6826 use_rhythmic_rodent = true;
6833 if (use_rhythmic_rodent) {
6834 show_rhythm_ferret ();
6838 AnalysisFeatureList::const_iterator x;
6840 pl->clear_changes ();
6841 pl->clear_owned_changes ();
6843 x = positions.begin();
6845 if (x == positions.end()) {
6850 pl->remove_region (r);
6854 framepos_t rstart = r->first_frame ();
6855 framepos_t rend = r->last_frame ();
6857 while (x != positions.end()) {
6859 /* deal with positons that are out of scope of present region bounds */
6860 if (*x <= rstart || *x > rend) {
6865 /* file start = original start + how far we from the initial position ? */
6867 framepos_t file_start = r->start() + pos;
6869 /* length = next position - current position */
6871 framepos_t len = (*x) - pos - rstart;
6873 /* XXX we do we really want to allow even single-sample regions?
6874 * shouldn't we have some kind of lower limit on region size?
6883 if (RegionFactory::region_name (new_name, r->name())) {
6887 /* do NOT announce new regions 1 by one, just wait till they are all done */
6891 plist.add (ARDOUR::Properties::start, file_start);
6892 plist.add (ARDOUR::Properties::length, len);
6893 plist.add (ARDOUR::Properties::name, new_name);
6894 plist.add (ARDOUR::Properties::layer, 0);
6895 // TODO set transients_offset
6897 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6898 /* because we set annouce to false, manually add the new region to the
6901 RegionFactory::map_add (nr);
6903 pl->add_region (nr, rstart + pos);
6906 new_regions.push_front(nr);
6915 RegionFactory::region_name (new_name, r->name());
6917 /* Add the final region */
6920 plist.add (ARDOUR::Properties::start, r->start() + pos);
6921 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6922 plist.add (ARDOUR::Properties::name, new_name);
6923 plist.add (ARDOUR::Properties::layer, 0);
6925 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6926 /* because we set annouce to false, manually add the new region to the
6929 RegionFactory::map_add (nr);
6930 pl->add_region (nr, r->position() + pos);
6933 new_regions.push_front(nr);
6938 /* We might have removed regions, which alters other regions' layering_index,
6939 so we need to do a recursive diff here.
6941 vector<Command*> cmds;
6943 _session->add_commands (cmds);
6945 _session->add_command (new StatefulDiffCommand (pl));
6949 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6950 set_selected_regionview_from_region_list ((*i), Selection::Add);
6956 Editor::place_transient()
6962 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6968 framepos_t where = get_preferred_edit_position();
6970 begin_reversible_command (_("place transient"));
6972 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6973 (*r)->region()->add_transient(where);
6976 commit_reversible_command ();
6980 Editor::remove_transient(ArdourCanvas::Item* item)
6986 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6989 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6990 _arv->remove_transient (*(float*) _line->get_data ("position"));
6994 Editor::snap_regions_to_grid ()
6996 list <boost::shared_ptr<Playlist > > used_playlists;
6998 RegionSelection rs = get_regions_from_selection_and_entered ();
7000 if (!_session || rs.empty()) {
7004 begin_reversible_command (_("snap regions to grid"));
7006 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7008 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7010 if (!pl->frozen()) {
7011 /* we haven't seen this playlist before */
7013 /* remember used playlists so we can thaw them later */
7014 used_playlists.push_back(pl);
7018 framepos_t start_frame = (*r)->region()->first_frame ();
7019 snap_to (start_frame);
7020 (*r)->region()->set_position (start_frame);
7023 while (used_playlists.size() > 0) {
7024 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7026 used_playlists.pop_front();
7029 commit_reversible_command ();
7033 Editor::close_region_gaps ()
7035 list <boost::shared_ptr<Playlist > > used_playlists;
7037 RegionSelection rs = get_regions_from_selection_and_entered ();
7039 if (!_session || rs.empty()) {
7043 Dialog dialog (_("Close Region Gaps"));
7046 table.set_spacings (12);
7047 table.set_border_width (12);
7048 Label* l = manage (left_aligned_label (_("Crossfade length")));
7049 table.attach (*l, 0, 1, 0, 1);
7051 SpinButton spin_crossfade (1, 0);
7052 spin_crossfade.set_range (0, 15);
7053 spin_crossfade.set_increments (1, 1);
7054 spin_crossfade.set_value (5);
7055 table.attach (spin_crossfade, 1, 2, 0, 1);
7057 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7059 l = manage (left_aligned_label (_("Pull-back length")));
7060 table.attach (*l, 0, 1, 1, 2);
7062 SpinButton spin_pullback (1, 0);
7063 spin_pullback.set_range (0, 100);
7064 spin_pullback.set_increments (1, 1);
7065 spin_pullback.set_value(30);
7066 table.attach (spin_pullback, 1, 2, 1, 2);
7068 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7070 dialog.get_vbox()->pack_start (table);
7071 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7072 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7075 if (dialog.run () == RESPONSE_CANCEL) {
7079 framepos_t crossfade_len = spin_crossfade.get_value();
7080 framepos_t pull_back_frames = spin_pullback.get_value();
7082 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7083 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7085 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7087 begin_reversible_command (_("close region gaps"));
7090 boost::shared_ptr<Region> last_region;
7092 rs.sort_by_position_and_track();
7094 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7096 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7098 if (!pl->frozen()) {
7099 /* we haven't seen this playlist before */
7101 /* remember used playlists so we can thaw them later */
7102 used_playlists.push_back(pl);
7106 framepos_t position = (*r)->region()->position();
7108 if (idx == 0 || position < last_region->position()){
7109 last_region = (*r)->region();
7114 (*r)->region()->trim_front( (position - pull_back_frames));
7115 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7117 last_region = (*r)->region();
7122 while (used_playlists.size() > 0) {
7123 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7125 used_playlists.pop_front();
7128 commit_reversible_command ();
7132 Editor::tab_to_transient (bool forward)
7134 AnalysisFeatureList positions;
7136 RegionSelection rs = get_regions_from_selection_and_entered ();
7142 framepos_t pos = _session->audible_frame ();
7144 if (!selection->tracks.empty()) {
7146 /* don't waste time searching for transients in duplicate playlists.
7149 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7151 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7153 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7156 boost::shared_ptr<Track> tr = rtv->track();
7158 boost::shared_ptr<Playlist> pl = tr->playlist ();
7160 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7163 positions.push_back (result);
7176 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7177 (*r)->region()->get_transients (positions);
7181 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7184 AnalysisFeatureList::iterator x;
7186 for (x = positions.begin(); x != positions.end(); ++x) {
7192 if (x != positions.end ()) {
7193 _session->request_locate (*x);
7197 AnalysisFeatureList::reverse_iterator x;
7199 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7205 if (x != positions.rend ()) {
7206 _session->request_locate (*x);
7212 Editor::playhead_forward_to_grid ()
7218 framepos_t pos = playhead_cursor->current_frame ();
7219 if (pos < max_framepos - 1) {
7221 snap_to_internal (pos, RoundUpAlways, false);
7222 _session->request_locate (pos);
7228 Editor::playhead_backward_to_grid ()
7234 framepos_t pos = playhead_cursor->current_frame ();
7237 snap_to_internal (pos, RoundDownAlways, false);
7238 _session->request_locate (pos);
7243 Editor::set_track_height (Height h)
7245 TrackSelection& ts (selection->tracks);
7247 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7248 (*x)->set_height_enum (h);
7253 Editor::toggle_tracks_active ()
7255 TrackSelection& ts (selection->tracks);
7257 bool target = false;
7263 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7264 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7268 target = !rtv->_route->active();
7271 rtv->_route->set_active (target, this);
7277 Editor::remove_tracks ()
7279 /* this will delete GUI objects that may be the subject of an event
7280 handler in which this method is called. Defer actual deletion to the
7281 next idle callback, when all event handling is finished.
7283 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7287 Editor::idle_remove_tracks ()
7289 Session::StateProtector sp (_session);
7291 return false; /* do not call again */
7295 Editor::_remove_tracks ()
7297 TrackSelection& ts (selection->tracks);
7303 vector<string> choices;
7307 const char* trackstr;
7309 vector<boost::shared_ptr<Route> > routes;
7310 bool special_bus = false;
7312 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7313 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7317 if (rtv->is_track()) {
7322 routes.push_back (rtv->_route);
7324 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7329 if (special_bus && !Config->get_allow_special_bus_removal()) {
7330 MessageDialog msg (_("That would be bad news ...."),
7334 msg.set_secondary_text (string_compose (_(
7335 "Removing the master or monitor bus is such a bad idea\n\
7336 that %1 is not going to allow it.\n\
7338 If you really want to do this sort of thing\n\
7339 edit your ardour.rc file to set the\n\
7340 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7347 if (ntracks + nbusses == 0) {
7351 trackstr = P_("track", "tracks", ntracks);
7352 busstr = P_("bus", "busses", nbusses);
7356 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7357 "(You may also lose the playlists associated with the %2)\n\n"
7358 "This action cannot be undone, and the session file will be overwritten!"),
7359 ntracks, trackstr, nbusses, busstr);
7361 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7362 "(You may also lose the playlists associated with the %2)\n\n"
7363 "This action cannot be undone, and the session file will be overwritten!"),
7366 } else if (nbusses) {
7367 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7368 "This action cannot be undone, and the session file will be overwritten"),
7372 choices.push_back (_("No, do nothing."));
7373 if (ntracks + nbusses > 1) {
7374 choices.push_back (_("Yes, remove them."));
7376 choices.push_back (_("Yes, remove it."));
7381 title = string_compose (_("Remove %1"), trackstr);
7383 title = string_compose (_("Remove %1"), busstr);
7386 Choice prompter (title, prompt, choices);
7388 if (prompter.run () != 1) {
7392 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7393 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7394 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7395 * likely because deletion requires selection) this will call
7396 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7397 * It's likewise likely that the route that has just been displayed in the
7398 * Editor-Mixer will be next in line for deletion.
7400 * So simply switch to the master-bus (if present)
7402 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7403 if ((*i)->stripable ()->is_master ()) {
7404 set_selected_mixer_strip (*(*i));
7410 Mixer_UI::instance()->selection().block_routes_changed (true);
7411 selection->block_tracks_changed (true);
7413 DisplaySuspender ds;
7414 boost::shared_ptr<RouteList> rl (new RouteList);
7415 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7418 _session->remove_routes (rl);
7420 /* TrackSelection and RouteList leave scope,
7421 * destructors are called,
7422 * diskstream drops references, save_state is called (again for every track)
7424 selection->block_tracks_changed (false);
7425 Mixer_UI::instance()->selection().block_routes_changed (false);
7426 selection->TracksChanged (); /* EMIT SIGNAL */
7430 Editor::do_insert_time ()
7432 if (selection->tracks.empty()) {
7436 InsertRemoveTimeDialog d (*this);
7437 int response = d.run ();
7439 if (response != RESPONSE_OK) {
7443 if (d.distance() == 0) {
7450 d.intersected_region_action (),
7454 d.move_glued_markers(),
7455 d.move_locked_markers(),
7461 Editor::insert_time (
7462 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7463 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7467 if (Config->get_edit_mode() == Lock) {
7470 bool in_command = false;
7472 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7474 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7478 /* don't operate on any playlist more than once, which could
7479 * happen if "all playlists" is enabled, but there is more
7480 * than 1 track using playlists "from" a given track.
7483 set<boost::shared_ptr<Playlist> > pl;
7485 if (all_playlists) {
7486 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7487 if (rtav && rtav->track ()) {
7488 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7489 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7494 if ((*x)->playlist ()) {
7495 pl.insert ((*x)->playlist ());
7499 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7501 (*i)->clear_changes ();
7502 (*i)->clear_owned_changes ();
7504 if (opt == SplitIntersected) {
7505 /* non musical split */
7506 (*i)->split (pos, 0);
7509 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7512 begin_reversible_command (_("insert time"));
7515 vector<Command*> cmds;
7517 _session->add_commands (cmds);
7519 _session->add_command (new StatefulDiffCommand (*i));
7523 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7526 begin_reversible_command (_("insert time"));
7529 rtav->route ()->shift (pos, frames);
7536 XMLNode& before (_session->locations()->get_state());
7537 Locations::LocationList copy (_session->locations()->list());
7539 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7541 Locations::LocationList::const_iterator tmp;
7543 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7544 bool const was_locked = (*i)->locked ();
7545 if (locked_markers_too) {
7549 if ((*i)->start() >= pos) {
7550 // move end first, in case we're moving by more than the length of the range
7551 if (!(*i)->is_mark()) {
7552 (*i)->set_end ((*i)->end() + frames);
7554 (*i)->set_start ((*i)->start() + frames);
7566 begin_reversible_command (_("insert time"));
7569 XMLNode& after (_session->locations()->get_state());
7570 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7576 begin_reversible_command (_("insert time"));
7579 XMLNode& before (_session->tempo_map().get_state());
7580 _session->tempo_map().insert_time (pos, frames);
7581 XMLNode& after (_session->tempo_map().get_state());
7582 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7586 commit_reversible_command ();
7591 Editor::do_remove_time ()
7593 if (selection->tracks.empty()) {
7597 InsertRemoveTimeDialog d (*this, true);
7599 int response = d.run ();
7601 if (response != RESPONSE_OK) {
7605 framecnt_t distance = d.distance();
7607 if (distance == 0) {
7617 d.move_glued_markers(),
7618 d.move_locked_markers(),
7624 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7625 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7627 if (Config->get_edit_mode() == Lock) {
7628 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7631 bool in_command = false;
7633 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7635 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7639 XMLNode &before = pl->get_state();
7641 std::list<AudioRange> rl;
7642 AudioRange ar(pos, pos+frames, 0);
7645 pl->shift (pos, -frames, true, ignore_music_glue);
7648 begin_reversible_command (_("remove time"));
7651 XMLNode &after = pl->get_state();
7653 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7657 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7660 begin_reversible_command (_("remove time"));
7663 rtav->route ()->shift (pos, -frames);
7667 std::list<Location*> loc_kill_list;
7672 XMLNode& before (_session->locations()->get_state());
7673 Locations::LocationList copy (_session->locations()->list());
7675 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7676 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7678 bool const was_locked = (*i)->locked ();
7679 if (locked_markers_too) {
7683 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7684 if ((*i)->end() >= pos
7685 && (*i)->end() < pos+frames
7686 && (*i)->start() >= pos
7687 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7689 loc_kill_list.push_back(*i);
7690 } else { // only start or end is included, try to do the right thing
7691 // move start before moving end, to avoid trying to move the end to before the start
7692 // if we're removing more time than the length of the range
7693 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7694 // start is within cut
7695 (*i)->set_start (pos); // bring the start marker to the beginning of the cut
7697 } else if ((*i)->start() >= pos+frames) {
7698 // start (and thus entire range) lies beyond end of cut
7699 (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7702 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7703 // end is inside cut
7704 (*i)->set_end (pos); // bring the end to the cut
7706 } else if ((*i)->end() >= pos+frames) {
7707 // end is beyond end of cut
7708 (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7713 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7714 loc_kill_list.push_back(*i);
7716 } else if ((*i)->start() >= pos) {
7717 (*i)->set_start ((*i)->start() -frames);
7727 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7728 _session->locations()->remove( *i );
7733 begin_reversible_command (_("remove time"));
7736 XMLNode& after (_session->locations()->get_state());
7737 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7742 XMLNode& before (_session->tempo_map().get_state());
7744 if (_session->tempo_map().remove_time (pos, frames) ) {
7746 begin_reversible_command (_("remove time"));
7749 XMLNode& after (_session->tempo_map().get_state());
7750 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7755 commit_reversible_command ();
7760 Editor::fit_selection ()
7762 if (!selection->tracks.empty()) {
7763 fit_tracks (selection->tracks);
7767 /* no selected tracks - use tracks with selected regions */
7769 if (!selection->regions.empty()) {
7770 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7771 tvl.push_back (&(*r)->get_time_axis_view ());
7777 } else if (internal_editing()) {
7778 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7781 if (entered_track) {
7782 tvl.push_back (entered_track);
7791 Editor::fit_tracks (TrackViewList & tracks)
7793 if (tracks.empty()) {
7797 uint32_t child_heights = 0;
7798 int visible_tracks = 0;
7800 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7802 if (!(*t)->marked_for_display()) {
7806 child_heights += (*t)->effective_height() - (*t)->current_height();
7810 /* compute the per-track height from:
7812 total canvas visible height -
7813 height that will be taken by visible children of selected
7814 tracks - height of the ruler/hscroll area
7816 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7817 double first_y_pos = DBL_MAX;
7819 if (h < TimeAxisView::preset_height (HeightSmall)) {
7820 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7821 /* too small to be displayed */
7825 undo_visual_stack.push_back (current_visual_state (true));
7826 PBD::Unwinder<bool> nsv (no_save_visual, true);
7828 /* build a list of all tracks, including children */
7831 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7833 TimeAxisView::Children c = (*i)->get_child_list ();
7834 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7835 all.push_back (j->get());
7840 // find selection range.
7841 // if someone knows how to user TrackViewList::iterator for this
7843 int selected_top = -1;
7844 int selected_bottom = -1;
7846 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7847 if ((*t)->marked_for_display ()) {
7848 if (tracks.contains(*t)) {
7849 if (selected_top == -1) {
7852 selected_bottom = i;
7858 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7859 if ((*t)->marked_for_display ()) {
7860 if (tracks.contains(*t)) {
7861 (*t)->set_height (h);
7862 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7864 if (i > selected_top && i < selected_bottom) {
7865 hide_track_in_display (*t);
7872 set the controls_layout height now, because waiting for its size
7873 request signal handler will cause the vertical adjustment setting to fail
7876 controls_layout.property_height () = _full_canvas_height;
7877 vertical_adjustment.set_value (first_y_pos);
7879 redo_visual_stack.push_back (current_visual_state (true));
7881 visible_tracks_selector.set_text (_("Sel"));
7885 Editor::save_visual_state (uint32_t n)
7887 while (visual_states.size() <= n) {
7888 visual_states.push_back (0);
7891 if (visual_states[n] != 0) {
7892 delete visual_states[n];
7895 visual_states[n] = current_visual_state (true);
7900 Editor::goto_visual_state (uint32_t n)
7902 if (visual_states.size() <= n) {
7906 if (visual_states[n] == 0) {
7910 use_visual_state (*visual_states[n]);
7914 Editor::start_visual_state_op (uint32_t n)
7916 save_visual_state (n);
7918 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7920 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7921 pup->set_text (buf);
7926 Editor::cancel_visual_state_op (uint32_t n)
7928 goto_visual_state (n);
7932 Editor::toggle_region_mute ()
7934 if (_ignore_region_action) {
7938 RegionSelection rs = get_regions_from_selection_and_entered ();
7944 if (rs.size() > 1) {
7945 begin_reversible_command (_("mute regions"));
7947 begin_reversible_command (_("mute region"));
7950 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7952 (*i)->region()->playlist()->clear_changes ();
7953 (*i)->region()->set_muted (!(*i)->region()->muted ());
7954 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7958 commit_reversible_command ();
7962 Editor::combine_regions ()
7964 /* foreach track with selected regions, take all selected regions
7965 and join them into a new region containing the subregions (as a
7969 typedef set<RouteTimeAxisView*> RTVS;
7972 if (selection->regions.empty()) {
7976 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7977 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7980 tracks.insert (rtv);
7984 begin_reversible_command (_("combine regions"));
7986 vector<RegionView*> new_selection;
7988 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7991 if ((rv = (*i)->combine_regions ()) != 0) {
7992 new_selection.push_back (rv);
7996 selection->clear_regions ();
7997 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7998 selection->add (*i);
8001 commit_reversible_command ();
8005 Editor::uncombine_regions ()
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 (_("uncombine regions"));
8024 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
8025 (*i)->uncombine_regions ();
8028 commit_reversible_command ();
8032 Editor::toggle_midi_input_active (bool flip_others)
8035 boost::shared_ptr<RouteList> rl (new RouteList);
8037 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
8038 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
8044 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
8047 rl->push_back (rtav->route());
8048 onoff = !mt->input_active();
8052 _session->set_exclusive_input_active (rl, onoff, flip_others);
8055 static bool ok_fine (GdkEventAny*) { return true; }
8061 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8063 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8064 lock_dialog->get_vbox()->pack_start (*padlock);
8065 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8067 ArdourButton* b = manage (new ArdourButton);
8068 b->set_name ("lock button");
8069 b->set_text (_("Click to unlock"));
8070 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8071 lock_dialog->get_vbox()->pack_start (*b);
8073 lock_dialog->get_vbox()->show_all ();
8074 lock_dialog->set_size_request (200, 200);
8077 delete _main_menu_disabler;
8078 _main_menu_disabler = new MainMenuDisabler;
8080 lock_dialog->present ();
8082 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8088 lock_dialog->hide ();
8090 delete _main_menu_disabler;
8091 _main_menu_disabler = 0;
8093 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8094 start_lock_event_timing ();
8099 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8101 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8105 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8107 Timers::TimerSuspender t;
8108 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8109 Gtkmm2ext::UI::instance()->flush_pending (1);
8113 Editor::bring_all_sources_into_session ()
8120 ArdourDialog w (_("Moving embedded files into session folder"));
8121 w.get_vbox()->pack_start (msg);
8124 /* flush all pending GUI events because we're about to start copying
8128 Timers::TimerSuspender t;
8129 Gtkmm2ext::UI::instance()->flush_pending (3);
8133 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));