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::get_selection_extents (framepos_t &start, framepos_t &end) const
1928 start = max_framepos;
1932 //ToDo: if notes are selected, set extents to that selection
1934 //ToDo: if control points are selected, set extents to that selection
1936 if ( !selection->regions.empty() ) {
1937 RegionSelection rs = get_regions_from_selection_and_entered ();
1939 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
1941 if ((*i)->region()->position() < start) {
1942 start = (*i)->region()->position();
1945 if ((*i)->region()->last_frame() + 1 > end) {
1946 end = (*i)->region()->last_frame() + 1;
1950 } else if (!selection->time.empty()) {
1951 start = selection->time.start();
1952 end = selection->time.end_frame();
1954 ret = false; //no selection found
1957 if ((start == 0 && end == 0) || end < start) {
1966 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
1968 if (!selection) return;
1970 //ToDo: if notes are selected, zoom to that
1972 //ToDo: if control points are selected, zoom to that
1974 if (axes == Horizontal || axes == Both) {
1976 framepos_t start, end;
1977 if (get_selection_extents (start, end)) {
1978 calc_extra_zoom_edges (start, end);
1979 temporal_zoom_by_frame (start, end);
1983 if (axes == Vertical || axes == Both) {
1989 Editor::temporal_zoom_session ()
1991 ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_session)
1994 framecnt_t start = _session->current_start_frame();
1995 framecnt_t end = _session->current_end_frame();
1997 if (_session->actively_recording () ) {
1998 framepos_t cur = playhead_cursor->current_frame ();
2000 /* recording beyond the end marker; zoom out
2001 * by 5 seconds more so that if 'follow
2002 * playhead' is active we don't immediately
2005 end = cur + _session->frame_rate() * 5;
2009 if ((start == 0 && end == 0) || end < start) {
2013 calc_extra_zoom_edges(start, end);
2015 temporal_zoom_by_frame (start, end);
2020 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
2022 if (!_session) return;
2024 if ((start == 0 && end == 0) || end < start) {
2028 framepos_t range = end - start;
2030 const framecnt_t new_fpp = (framecnt_t) ceil ((double) range / (double) _visible_canvas_width);
2032 framepos_t new_page = range;
2033 framepos_t middle = (framepos_t) floor ((double) start + ((double) range / 2.0f));
2034 framepos_t new_leftmost = (framepos_t) floor ((double) middle - ((double) new_page / 2.0f));
2036 if (new_leftmost > middle) {
2040 if (new_leftmost < 0) {
2044 reposition_and_zoom (new_leftmost, new_fpp);
2048 Editor::temporal_zoom_to_frame (bool coarser, framepos_t frame)
2054 framecnt_t range_before = frame - leftmost_frame;
2058 if (samples_per_pixel <= 1) {
2061 new_spp = samples_per_pixel + (samples_per_pixel/2);
2063 range_before += range_before/2;
2065 if (samples_per_pixel >= 1) {
2066 new_spp = samples_per_pixel - (samples_per_pixel/2);
2068 /* could bail out here since we cannot zoom any finer,
2069 but leave that to the equality test below
2071 new_spp = samples_per_pixel;
2074 range_before -= range_before/2;
2077 if (new_spp == samples_per_pixel) {
2081 /* zoom focus is automatically taken as @param frame when this
2085 framepos_t new_leftmost = frame - (framepos_t)range_before;
2087 if (new_leftmost > frame) {
2091 if (new_leftmost < 0) {
2095 reposition_and_zoom (new_leftmost, new_spp);
2100 Editor::choose_new_marker_name(string &name) {
2102 if (!UIConfiguration::instance().get_name_new_markers()) {
2103 /* don't prompt user for a new name */
2107 ArdourPrompter dialog (true);
2109 dialog.set_prompt (_("New Name:"));
2111 dialog.set_title (_("New Location Marker"));
2113 dialog.set_name ("MarkNameWindow");
2114 dialog.set_size_request (250, -1);
2115 dialog.set_position (Gtk::WIN_POS_MOUSE);
2117 dialog.add_button (Stock::OK, RESPONSE_ACCEPT);
2118 dialog.set_initial_text (name);
2122 switch (dialog.run ()) {
2123 case RESPONSE_ACCEPT:
2129 dialog.get_result(name);
2136 Editor::add_location_from_selection ()
2140 if (selection->time.empty()) {
2144 if (_session == 0 || clicked_axisview == 0) {
2148 framepos_t start = selection->time[clicked_selection].start;
2149 framepos_t end = selection->time[clicked_selection].end;
2151 _session->locations()->next_available_name(rangename,"selection");
2152 Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
2154 begin_reversible_command (_("add marker"));
2156 XMLNode &before = _session->locations()->get_state();
2157 _session->locations()->add (location, true);
2158 XMLNode &after = _session->locations()->get_state();
2159 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2161 commit_reversible_command ();
2165 Editor::add_location_mark (framepos_t where)
2169 select_new_marker = true;
2171 _session->locations()->next_available_name(markername,"mark");
2172 if (!choose_new_marker_name(markername)) {
2175 Location *location = new Location (*_session, where, where, markername, Location::IsMark);
2176 begin_reversible_command (_("add marker"));
2178 XMLNode &before = _session->locations()->get_state();
2179 _session->locations()->add (location, true);
2180 XMLNode &after = _session->locations()->get_state();
2181 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2183 commit_reversible_command ();
2187 Editor::set_session_start_from_playhead ()
2193 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2194 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2196 XMLNode &before = loc->get_state();
2198 _session->set_session_extents ( _session->audible_frame(), loc->end() );
2200 XMLNode &after = loc->get_state();
2202 begin_reversible_command (_("Set session start"));
2204 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2206 commit_reversible_command ();
2211 Editor::set_session_end_from_playhead ()
2217 if ((loc = _session->locations()->session_range_location()) == 0) { //should never happen
2218 _session->set_session_extents ( _session->audible_frame(), _session->audible_frame() );
2220 XMLNode &before = loc->get_state();
2222 _session->set_session_extents ( loc->start(), _session->audible_frame() );
2224 XMLNode &after = loc->get_state();
2226 begin_reversible_command (_("Set session start"));
2228 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
2230 commit_reversible_command ();
2233 _session->set_end_is_free (false);
2238 Editor::toggle_location_at_playhead_cursor ()
2240 if (!do_remove_location_at_playhead_cursor())
2242 add_location_from_playhead_cursor();
2247 Editor::add_location_from_playhead_cursor ()
2249 add_location_mark (_session->audible_frame());
2253 Editor::do_remove_location_at_playhead_cursor ()
2255 bool removed = false;
2258 XMLNode &before = _session->locations()->get_state();
2260 //find location(s) at this time
2261 Locations::LocationList locs;
2262 _session->locations()->find_all_between (_session->audible_frame(), _session->audible_frame()+1, locs, Location::Flags(0));
2263 for (Locations::LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
2264 if ((*i)->is_mark()) {
2265 _session->locations()->remove (*i);
2272 begin_reversible_command (_("remove marker"));
2273 XMLNode &after = _session->locations()->get_state();
2274 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2275 commit_reversible_command ();
2282 Editor::remove_location_at_playhead_cursor ()
2284 do_remove_location_at_playhead_cursor ();
2287 /** Add a range marker around each selected region */
2289 Editor::add_locations_from_region ()
2291 RegionSelection rs = get_regions_from_selection_and_entered ();
2296 bool commit = false;
2298 XMLNode &before = _session->locations()->get_state();
2300 for (RegionSelection::iterator i = rs.begin (); i != rs.end (); ++i) {
2302 boost::shared_ptr<Region> region = (*i)->region ();
2304 Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
2306 _session->locations()->add (location, true);
2311 begin_reversible_command (selection->regions.size () > 1 ? _("add markers") : _("add marker"));
2312 XMLNode &after = _session->locations()->get_state();
2313 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2314 commit_reversible_command ();
2318 /** Add a single range marker around all selected regions */
2320 Editor::add_location_from_region ()
2322 RegionSelection rs = get_regions_from_selection_and_entered ();
2328 XMLNode &before = _session->locations()->get_state();
2332 if (rs.size() > 1) {
2333 _session->locations()->next_available_name(markername, "regions");
2335 RegionView* rv = *(rs.begin());
2336 boost::shared_ptr<Region> region = rv->region();
2337 markername = region->name();
2340 if (!choose_new_marker_name(markername)) {
2344 // single range spanning all selected
2345 Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
2346 _session->locations()->add (location, true);
2348 begin_reversible_command (_("add marker"));
2349 XMLNode &after = _session->locations()->get_state();
2350 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2351 commit_reversible_command ();
2357 Editor::jump_forward_to_mark ()
2363 framepos_t pos = _session->locations()->first_mark_after (playhead_cursor->current_frame());
2369 _session->request_locate (pos, _session->transport_rolling());
2373 Editor::jump_backward_to_mark ()
2379 framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
2385 _session->request_locate (pos, _session->transport_rolling());
2391 framepos_t const pos = _session->audible_frame ();
2394 _session->locations()->next_available_name (markername, "mark");
2396 if (!choose_new_marker_name (markername)) {
2400 _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
2404 Editor::clear_markers ()
2407 begin_reversible_command (_("clear markers"));
2409 XMLNode &before = _session->locations()->get_state();
2410 _session->locations()->clear_markers ();
2411 XMLNode &after = _session->locations()->get_state();
2412 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2414 commit_reversible_command ();
2419 Editor::clear_ranges ()
2422 begin_reversible_command (_("clear ranges"));
2424 XMLNode &before = _session->locations()->get_state();
2426 _session->locations()->clear_ranges ();
2428 XMLNode &after = _session->locations()->get_state();
2429 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2431 commit_reversible_command ();
2436 Editor::clear_locations ()
2438 begin_reversible_command (_("clear locations"));
2440 XMLNode &before = _session->locations()->get_state();
2441 _session->locations()->clear ();
2442 XMLNode &after = _session->locations()->get_state();
2443 _session->add_command(new MementoCommand<Locations>(*(_session->locations()), &before, &after));
2445 commit_reversible_command ();
2449 Editor::unhide_markers ()
2451 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2452 Location *l = (*i).first;
2453 if (l->is_hidden() && l->is_mark()) {
2454 l->set_hidden(false, this);
2460 Editor::unhide_ranges ()
2462 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
2463 Location *l = (*i).first;
2464 if (l->is_hidden() && l->is_range_marker()) {
2465 l->set_hidden(false, this);
2470 /* INSERT/REPLACE */
2473 Editor::insert_region_list_selection (float times)
2475 RouteTimeAxisView *tv = 0;
2476 boost::shared_ptr<Playlist> playlist;
2478 if (clicked_routeview != 0) {
2479 tv = clicked_routeview;
2480 } else if (!selection->tracks.empty()) {
2481 if ((tv = dynamic_cast<RouteTimeAxisView*>(selection->tracks.front())) == 0) {
2484 } else if (entered_track != 0) {
2485 if ((tv = dynamic_cast<RouteTimeAxisView*>(entered_track)) == 0) {
2492 if ((playlist = tv->playlist()) == 0) {
2496 boost::shared_ptr<Region> region = _regions->get_single_selection ();
2501 begin_reversible_command (_("insert region"));
2502 playlist->clear_changes ();
2503 playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times);
2504 if (Config->get_edit_mode() == Ripple)
2505 playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr<Region>());
2507 _session->add_command(new StatefulDiffCommand (playlist));
2508 commit_reversible_command ();
2511 /* BUILT-IN EFFECTS */
2514 Editor::reverse_selection ()
2519 /* GAIN ENVELOPE EDITING */
2522 Editor::edit_envelope ()
2529 Editor::transition_to_rolling (bool fwd)
2535 if (_session->config.get_external_sync()) {
2536 switch (Config->get_sync_source()) {
2540 /* transport controlled by the master */
2545 if (_session->is_auditioning()) {
2546 _session->cancel_audition ();
2550 _session->request_transport_speed (fwd ? 1.0f : -1.0f);
2554 Editor::play_from_start ()
2556 _session->request_locate (_session->current_start_frame(), true);
2560 Editor::play_from_edit_point ()
2562 _session->request_locate (get_preferred_edit_position(), true);
2566 Editor::play_from_edit_point_and_return ()
2568 framepos_t start_frame;
2569 framepos_t return_frame;
2571 start_frame = get_preferred_edit_position ( EDIT_IGNORE_PHEAD );
2573 if (_session->transport_rolling()) {
2574 _session->request_locate (start_frame, false);
2578 /* don't reset the return frame if its already set */
2580 if ((return_frame = _session->requested_return_frame()) < 0) {
2581 return_frame = _session->audible_frame();
2584 if (start_frame >= 0) {
2585 _session->request_roll_at_and_return (start_frame, return_frame);
2590 Editor::play_selection ()
2592 framepos_t start, end;
2593 if (!get_selection_extents ( start, end))
2596 AudioRange ar (start, end, 0);
2597 list<AudioRange> lar;
2600 _session->request_play_range (&lar, true);
2604 Editor::get_preroll ()
2606 return Config->get_preroll_seconds() * _session->frame_rate();
2611 Editor::maybe_locate_with_edit_preroll ( framepos_t location )
2613 if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
2616 location -= get_preroll();
2618 //don't try to locate before the beginning of time
2622 //if follow_playhead is on, keep the playhead on the screen
2623 if ( _follow_playhead )
2624 if ( location < leftmost_frame )
2625 location = leftmost_frame;
2627 _session->request_locate( location );
2631 Editor::play_with_preroll ()
2634 framepos_t preroll = get_preroll();
2636 framepos_t start, end;
2637 if (!get_selection_extents ( start, end))
2640 if (start > preroll)
2641 start = start - preroll;
2643 end = end + preroll; //"post-roll"
2645 AudioRange ar (start, end, 0);
2646 list<AudioRange> lar;
2649 _session->request_play_range (&lar, true);
2654 Editor::play_location (Location& location)
2656 if (location.start() <= location.end()) {
2660 _session->request_bounded_roll (location.start(), location.end());
2664 Editor::loop_location (Location& location)
2666 if (location.start() <= location.end()) {
2672 if ((tll = transport_loop_location()) != 0) {
2673 tll->set (location.start(), location.end());
2675 // enable looping, reposition and start rolling
2676 _session->request_locate (tll->start(), true);
2677 _session->request_play_loop (true);
2682 Editor::do_layer_operation (LayerOperation op)
2684 if (selection->regions.empty ()) {
2688 bool const multiple = selection->regions.size() > 1;
2692 begin_reversible_command (_("raise regions"));
2694 begin_reversible_command (_("raise region"));
2700 begin_reversible_command (_("raise regions to top"));
2702 begin_reversible_command (_("raise region to top"));
2708 begin_reversible_command (_("lower regions"));
2710 begin_reversible_command (_("lower region"));
2716 begin_reversible_command (_("lower regions to bottom"));
2718 begin_reversible_command (_("lower region"));
2723 set<boost::shared_ptr<Playlist> > playlists = selection->regions.playlists ();
2724 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2725 (*i)->clear_owned_changes ();
2728 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
2729 boost::shared_ptr<Region> r = (*i)->region ();
2741 r->lower_to_bottom ();
2745 for (set<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
2746 vector<Command*> cmds;
2748 _session->add_commands (cmds);
2751 commit_reversible_command ();
2755 Editor::raise_region ()
2757 do_layer_operation (Raise);
2761 Editor::raise_region_to_top ()
2763 do_layer_operation (RaiseToTop);
2767 Editor::lower_region ()
2769 do_layer_operation (Lower);
2773 Editor::lower_region_to_bottom ()
2775 do_layer_operation (LowerToBottom);
2778 /** Show the region editor for the selected regions */
2780 Editor::show_region_properties ()
2782 selection->foreach_regionview (&RegionView::show_region_editor);
2785 /** Show the midi list editor for the selected MIDI regions */
2787 Editor::show_midi_list_editor ()
2789 selection->foreach_midi_regionview (&MidiRegionView::show_list_editor);
2793 Editor::rename_region ()
2795 RegionSelection rs = get_regions_from_selection_and_entered ();
2801 ArdourDialog d (_("Rename Region"), true, false);
2803 Label label (_("New name:"));
2806 hbox.set_spacing (6);
2807 hbox.pack_start (label, false, false);
2808 hbox.pack_start (entry, true, true);
2810 d.get_vbox()->set_border_width (12);
2811 d.get_vbox()->pack_start (hbox, false, false);
2813 d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
2814 d.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
2816 d.set_size_request (300, -1);
2818 entry.set_text (rs.front()->region()->name());
2819 entry.select_region (0, -1);
2821 entry.signal_activate().connect (sigc::bind (sigc::mem_fun (d, &Dialog::response), RESPONSE_OK));
2827 int const ret = d.run();
2831 if (ret != RESPONSE_OK) {
2835 std::string str = entry.get_text();
2836 strip_whitespace_edges (str);
2838 rs.front()->region()->set_name (str);
2839 _regions->redisplay ();
2843 /** Start an audition of the first selected region */
2845 Editor::play_edit_range ()
2847 framepos_t start, end;
2849 if (get_edit_op_range (start, end)) {
2850 _session->request_bounded_roll (start, end);
2855 Editor::play_selected_region ()
2857 framepos_t start = max_framepos;
2860 RegionSelection rs = get_regions_from_selection_and_entered ();
2866 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
2867 if ((*i)->region()->position() < start) {
2868 start = (*i)->region()->position();
2870 if ((*i)->region()->last_frame() + 1 > end) {
2871 end = (*i)->region()->last_frame() + 1;
2875 _session->request_bounded_roll (start, end);
2879 Editor::audition_playlist_region_standalone (boost::shared_ptr<Region> region)
2881 _session->audition_region (region);
2885 Editor::region_from_selection ()
2887 if (clicked_axisview == 0) {
2891 if (selection->time.empty()) {
2895 framepos_t start = selection->time[clicked_selection].start;
2896 framepos_t end = selection->time[clicked_selection].end;
2898 TrackViewList tracks = get_tracks_for_range_action ();
2900 framepos_t selection_cnt = end - start + 1;
2902 for (TrackSelection::iterator i = tracks.begin(); i != tracks.end(); ++i) {
2903 boost::shared_ptr<Region> current;
2904 boost::shared_ptr<Playlist> pl;
2905 framepos_t internal_start;
2908 if ((pl = (*i)->playlist()) == 0) {
2912 if ((current = pl->top_region_at (start)) == 0) {
2916 internal_start = start - current->position();
2917 RegionFactory::region_name (new_name, current->name(), true);
2921 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2922 plist.add (ARDOUR::Properties::length, selection_cnt);
2923 plist.add (ARDOUR::Properties::name, new_name);
2924 plist.add (ARDOUR::Properties::layer, 0);
2926 boost::shared_ptr<Region> region (RegionFactory::create (current, plist));
2931 Editor::create_region_from_selection (vector<boost::shared_ptr<Region> >& new_regions)
2933 if (selection->time.empty() || selection->tracks.empty()) {
2937 framepos_t start, end;
2938 if (clicked_selection) {
2939 start = selection->time[clicked_selection].start;
2940 end = selection->time[clicked_selection].end;
2942 start = selection->time.start();
2943 end = selection->time.end_frame();
2946 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
2947 sort_track_selection (ts);
2949 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
2950 boost::shared_ptr<Region> current;
2951 boost::shared_ptr<Playlist> playlist;
2952 framepos_t internal_start;
2955 if ((playlist = (*i)->playlist()) == 0) {
2959 if ((current = playlist->top_region_at(start)) == 0) {
2963 internal_start = start - current->position();
2964 RegionFactory::region_name (new_name, current->name(), true);
2968 plist.add (ARDOUR::Properties::start, current->start() + internal_start);
2969 plist.add (ARDOUR::Properties::length, end - start + 1);
2970 plist.add (ARDOUR::Properties::name, new_name);
2972 new_regions.push_back (RegionFactory::create (current, plist));
2977 Editor::split_multichannel_region ()
2979 RegionSelection rs = get_regions_from_selection_and_entered ();
2985 vector< boost::shared_ptr<Region> > v;
2987 for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
2988 (*x)->region()->separate_by_channel (*_session, v);
2993 Editor::new_region_from_selection ()
2995 region_from_selection ();
2996 cancel_selection ();
3000 add_if_covered (RegionView* rv, const AudioRange* ar, RegionSelection* rs)
3002 switch (rv->region()->coverage (ar->start, ar->end - 1)) {
3003 // n.b. -1 because AudioRange::end is one past the end, but coverage expects inclusive ranges
3004 case Evoral::OverlapNone:
3012 * - selected tracks, or if there are none...
3013 * - tracks containing selected regions, or if there are none...
3018 Editor::get_tracks_for_range_action () const
3022 if (selection->tracks.empty()) {
3024 /* use tracks with selected regions */
3026 RegionSelection rs = selection->regions;
3028 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3029 TimeAxisView* tv = &(*i)->get_time_axis_view();
3031 if (!t.contains (tv)) {
3037 /* no regions and no tracks: use all tracks */
3043 t = selection->tracks;
3046 return t.filter_to_unique_playlists();
3050 Editor::separate_regions_between (const TimeSelection& ts)
3052 bool in_command = false;
3053 boost::shared_ptr<Playlist> playlist;
3054 RegionSelection new_selection;
3056 TrackViewList tmptracks = get_tracks_for_range_action ();
3057 sort_track_selection (tmptracks);
3059 for (TrackSelection::iterator i = tmptracks.begin(); i != tmptracks.end(); ++i) {
3061 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3067 if (!rtv->is_track()) {
3071 /* no edits to destructive tracks */
3073 if (rtv->track()->destructive()) {
3077 if ((playlist = rtv->playlist()) != 0) {
3079 playlist->clear_changes ();
3081 /* XXX need to consider musical time selections here at some point */
3083 double speed = rtv->track()->speed();
3085 for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
3087 sigc::connection c = rtv->view()->RegionViewAdded.connect (
3088 sigc::mem_fun(*this, &Editor::collect_new_region_view));
3090 latest_regionviews.clear ();
3092 playlist->partition ((framepos_t)((*t).start * speed),
3093 (framepos_t)((*t).end * speed), false);
3097 if (!latest_regionviews.empty()) {
3099 rtv->view()->foreach_regionview (sigc::bind (
3100 sigc::ptr_fun (add_if_covered),
3101 &(*t), &new_selection));
3104 begin_reversible_command (_("separate"));
3108 /* pick up changes to existing regions */
3110 vector<Command*> cmds;
3111 playlist->rdiff (cmds);
3112 _session->add_commands (cmds);
3114 /* pick up changes to the playlist itself (adds/removes)
3117 _session->add_command(new StatefulDiffCommand (playlist));
3124 // selection->set (new_selection);
3126 commit_reversible_command ();
3130 struct PlaylistState {
3131 boost::shared_ptr<Playlist> playlist;
3135 /** Take tracks from get_tracks_for_range_action and cut any regions
3136 * on those tracks so that the tracks are empty over the time
3140 Editor::separate_region_from_selection ()
3142 /* preferentially use *all* ranges in the time selection if we're in range mode
3143 to allow discontiguous operation, since get_edit_op_range() currently
3144 returns a single range.
3147 if (!selection->time.empty()) {
3149 separate_regions_between (selection->time);
3156 if (get_edit_op_range (start, end)) {
3158 AudioRange ar (start, end, 1);
3162 separate_regions_between (ts);
3168 Editor::separate_region_from_punch ()
3170 Location* loc = _session->locations()->auto_punch_location();
3172 separate_regions_using_location (*loc);
3177 Editor::separate_region_from_loop ()
3179 Location* loc = _session->locations()->auto_loop_location();
3181 separate_regions_using_location (*loc);
3186 Editor::separate_regions_using_location (Location& loc)
3188 if (loc.is_mark()) {
3192 AudioRange ar (loc.start(), loc.end(), 1);
3197 separate_regions_between (ts);
3200 /** Separate regions under the selected region */
3202 Editor::separate_under_selected_regions ()
3204 vector<PlaylistState> playlists;
3208 rs = get_regions_from_selection_and_entered();
3210 if (!_session || rs.empty()) {
3214 begin_reversible_command (_("separate region under"));
3216 list<boost::shared_ptr<Region> > regions_to_remove;
3218 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3219 // we can't just remove the region(s) in this loop because
3220 // this removes them from the RegionSelection, and they thus
3221 // disappear from underneath the iterator, and the ++i above
3222 // SEGVs in a puzzling fashion.
3224 // so, first iterate over the regions to be removed from rs and
3225 // add them to the regions_to_remove list, and then
3226 // iterate over the list to actually remove them.
3228 regions_to_remove.push_back ((*i)->region());
3231 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
3233 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
3236 // is this check necessary?
3240 vector<PlaylistState>::iterator i;
3242 //only take state if this is a new playlist.
3243 for (i = playlists.begin(); i != playlists.end(); ++i) {
3244 if ((*i).playlist == playlist) {
3249 if (i == playlists.end()) {
3251 PlaylistState before;
3252 before.playlist = playlist;
3253 before.before = &playlist->get_state();
3255 playlist->freeze ();
3256 playlists.push_back(before);
3259 //Partition on the region bounds
3260 playlist->partition ((*rl)->first_frame() - 1, (*rl)->last_frame() + 1, true);
3262 //Re-add region that was just removed due to the partition operation
3263 playlist->add_region( (*rl), (*rl)->first_frame() );
3266 vector<PlaylistState>::iterator pl;
3268 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
3269 (*pl).playlist->thaw ();
3270 _session->add_command(new MementoCommand<Playlist>(*(*pl).playlist, (*pl).before, &(*pl).playlist->get_state()));
3273 commit_reversible_command ();
3277 Editor::crop_region_to_selection ()
3279 if (!selection->time.empty()) {
3281 crop_region_to (selection->time.start(), selection->time.end_frame());
3288 if (get_edit_op_range (start, end)) {
3289 crop_region_to (start, end);
3296 Editor::crop_region_to (framepos_t start, framepos_t end)
3298 vector<boost::shared_ptr<Playlist> > playlists;
3299 boost::shared_ptr<Playlist> playlist;
3302 if (selection->tracks.empty()) {
3303 ts = track_views.filter_to_unique_playlists();
3305 ts = selection->tracks.filter_to_unique_playlists ();
3308 sort_track_selection (ts);
3310 for (TrackSelection::iterator i = ts.begin(); i != ts.end(); ++i) {
3312 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
3318 boost::shared_ptr<Track> t = rtv->track();
3320 if (t != 0 && ! t->destructive()) {
3322 if ((playlist = rtv->playlist()) != 0) {
3323 playlists.push_back (playlist);
3328 if (playlists.empty()) {
3333 framepos_t new_start;
3335 framecnt_t new_length;
3336 bool in_command = false;
3338 for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
3340 /* Only the top regions at start and end have to be cropped */
3341 boost::shared_ptr<Region> region_at_start = (*i)->top_region_at(start);
3342 boost::shared_ptr<Region> region_at_end = (*i)->top_region_at(end);
3344 vector<boost::shared_ptr<Region> > regions;
3346 if (region_at_start != 0) {
3347 regions.push_back (region_at_start);
3349 if (region_at_end != 0) {
3350 regions.push_back (region_at_end);
3353 /* now adjust lengths */
3354 for (vector<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
3356 pos = (*i)->position();
3357 new_start = max (start, pos);
3358 if (max_framepos - pos > (*i)->length()) {
3359 new_end = pos + (*i)->length() - 1;
3361 new_end = max_framepos;
3363 new_end = min (end, new_end);
3364 new_length = new_end - new_start + 1;
3367 begin_reversible_command (_("trim to selection"));
3370 (*i)->clear_changes ();
3371 (*i)->trim_to (new_start, new_length);
3372 _session->add_command (new StatefulDiffCommand (*i));
3377 commit_reversible_command ();
3382 Editor::region_fill_track ()
3384 boost::shared_ptr<Playlist> playlist;
3385 RegionSelection regions = get_regions_from_selection_and_entered ();
3386 RegionSelection foo;
3388 framepos_t const end = _session->current_end_frame ();
3390 if (regions.empty () || regions.end_frame () + 1 >= end) {
3394 framepos_t const start_frame = regions.start ();
3395 framepos_t const end_frame = regions.end_frame ();
3396 framecnt_t const gap = end_frame - start_frame + 1;
3398 begin_reversible_command (Operations::region_fill);
3400 selection->clear_regions ();
3402 for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
3404 boost::shared_ptr<Region> r ((*i)->region());
3406 TimeAxisView& tv = (*i)->get_time_axis_view();
3407 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
3408 latest_regionviews.clear ();
3409 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
3411 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
3412 playlist = (*i)->region()->playlist();
3413 playlist->clear_changes ();
3414 playlist->duplicate_until (r, position, gap, end);
3415 _session->add_command(new StatefulDiffCommand (playlist));
3419 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
3423 selection->set (foo);
3426 commit_reversible_command ();
3430 Editor::set_region_sync_position ()
3432 set_sync_point (get_preferred_edit_position (), get_regions_from_selection_and_edit_point ());
3436 Editor::set_sync_point (framepos_t where, const RegionSelection& rs)
3438 bool in_command = false;
3440 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ++r) {
3442 if (!(*r)->region()->covers (where)) {
3446 boost::shared_ptr<Region> region ((*r)->region());
3449 begin_reversible_command (_("set sync point"));
3453 region->clear_changes ();
3454 region->set_sync_position (where);
3455 _session->add_command(new StatefulDiffCommand (region));
3459 commit_reversible_command ();
3463 /** Remove the sync positions of the selection */
3465 Editor::remove_region_sync ()
3467 RegionSelection rs = get_regions_from_selection_and_entered ();
3473 begin_reversible_command (_("remove region sync"));
3475 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3477 (*i)->region()->clear_changes ();
3478 (*i)->region()->clear_sync_position ();
3479 _session->add_command(new StatefulDiffCommand ((*i)->region()));
3482 commit_reversible_command ();
3486 Editor::naturalize_region ()
3488 RegionSelection rs = get_regions_from_selection_and_entered ();
3494 if (rs.size() > 1) {
3495 begin_reversible_command (_("move regions to original position"));
3497 begin_reversible_command (_("move region to original position"));
3500 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
3501 (*i)->region()->clear_changes ();
3502 (*i)->region()->move_to_natural_position ();
3503 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3506 commit_reversible_command ();
3510 Editor::align_regions (RegionPoint what)
3512 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3518 begin_reversible_command (_("align selection"));
3520 framepos_t const position = get_preferred_edit_position ();
3522 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
3523 align_region_internal ((*i)->region(), what, position);
3526 commit_reversible_command ();
3529 struct RegionSortByTime {
3530 bool operator() (const RegionView* a, const RegionView* b) {
3531 return a->region()->position() < b->region()->position();
3536 Editor::align_regions_relative (RegionPoint point)
3538 RegionSelection const rs = get_regions_from_selection_and_edit_point ();
3544 framepos_t const position = get_preferred_edit_position ();
3546 framepos_t distance = 0;
3550 list<RegionView*> sorted;
3551 rs.by_position (sorted);
3553 boost::shared_ptr<Region> r ((*sorted.begin())->region());
3558 if (position > r->position()) {
3559 distance = position - r->position();
3561 distance = r->position() - position;
3567 if (position > r->last_frame()) {
3568 distance = position - r->last_frame();
3569 pos = r->position() + distance;
3571 distance = r->last_frame() - position;
3572 pos = r->position() - distance;
3578 pos = r->adjust_to_sync (position);
3579 if (pos > r->position()) {
3580 distance = pos - r->position();
3582 distance = r->position() - pos;
3588 if (pos == r->position()) {
3592 begin_reversible_command (_("align selection (relative)"));
3594 /* move first one specially */
3596 r->clear_changes ();
3597 r->set_position (pos);
3598 _session->add_command(new StatefulDiffCommand (r));
3600 /* move rest by the same amount */
3604 for (list<RegionView*>::iterator i = sorted.begin(); i != sorted.end(); ++i) {
3606 boost::shared_ptr<Region> region ((*i)->region());
3608 region->clear_changes ();
3611 region->set_position (region->position() + distance);
3613 region->set_position (region->position() - distance);
3616 _session->add_command(new StatefulDiffCommand (region));
3620 commit_reversible_command ();
3624 Editor::align_region (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3626 begin_reversible_command (_("align region"));
3627 align_region_internal (region, point, position);
3628 commit_reversible_command ();
3632 Editor::align_region_internal (boost::shared_ptr<Region> region, RegionPoint point, framepos_t position)
3634 region->clear_changes ();
3638 region->set_position (region->adjust_to_sync (position));
3642 if (position > region->length()) {
3643 region->set_position (position - region->length());
3648 region->set_position (position);
3652 _session->add_command(new StatefulDiffCommand (region));
3656 Editor::trim_region_front ()
3662 Editor::trim_region_back ()
3664 trim_region (false);
3668 Editor::trim_region (bool front)
3670 framepos_t where = get_preferred_edit_position();
3671 RegionSelection rs = get_regions_from_selection_and_edit_point ();
3677 begin_reversible_command (front ? _("trim front") : _("trim back"));
3679 for (list<RegionView*>::const_iterator i = rs.by_layer().begin(); i != rs.by_layer().end(); ++i) {
3680 if (!(*i)->region()->locked()) {
3682 (*i)->region()->clear_changes ();
3685 (*i)->region()->trim_front (where);
3686 maybe_locate_with_edit_preroll ( where );
3688 (*i)->region()->trim_end (where);
3689 maybe_locate_with_edit_preroll ( where );
3692 _session->add_command (new StatefulDiffCommand ((*i)->region()));
3696 commit_reversible_command ();
3699 /** Trim the end of the selected regions to the position of the edit cursor */
3701 Editor::trim_region_to_loop ()
3703 Location* loc = _session->locations()->auto_loop_location();
3707 trim_region_to_location (*loc, _("trim to loop"));
3711 Editor::trim_region_to_punch ()
3713 Location* loc = _session->locations()->auto_punch_location();
3717 trim_region_to_location (*loc, _("trim to punch"));
3721 Editor::trim_region_to_location (const Location& loc, const char* str)
3723 RegionSelection rs = get_regions_from_selection_and_entered ();
3724 bool in_command = false;
3726 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3727 RegionView* rv = (*x);
3729 /* require region to span proposed trim */
3730 switch (rv->region()->coverage (loc.start(), loc.end())) {
3731 case Evoral::OverlapInternal:
3737 RouteTimeAxisView* tav = dynamic_cast<RouteTimeAxisView*> (&rv->get_time_axis_view());
3746 if (tav->track() != 0) {
3747 speed = tav->track()->speed();
3750 start = session_frame_to_track_frame (loc.start(), speed);
3751 end = session_frame_to_track_frame (loc.end(), speed);
3753 rv->region()->clear_changes ();
3754 rv->region()->trim_to (start, (end - start));
3757 begin_reversible_command (str);
3760 _session->add_command(new StatefulDiffCommand (rv->region()));
3764 commit_reversible_command ();
3769 Editor::trim_region_to_previous_region_end ()
3771 return trim_to_region(false);
3775 Editor::trim_region_to_next_region_start ()
3777 return trim_to_region(true);
3781 Editor::trim_to_region(bool forward)
3783 RegionSelection rs = get_regions_from_selection_and_entered ();
3784 bool in_command = false;
3786 boost::shared_ptr<Region> next_region;
3788 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
3790 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (*x);
3796 AudioTimeAxisView* atav = dynamic_cast<AudioTimeAxisView*> (&arv->get_time_axis_view());
3804 if (atav->track() != 0) {
3805 speed = atav->track()->speed();
3809 boost::shared_ptr<Region> region = arv->region();
3810 boost::shared_ptr<Playlist> playlist (region->playlist());
3812 region->clear_changes ();
3816 next_region = playlist->find_next_region (region->first_frame(), Start, 1);
3822 region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
3823 arv->region_changed (PropertyChange (ARDOUR::Properties::length));
3827 next_region = playlist->find_next_region (region->first_frame(), Start, 0);
3833 region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
3835 arv->region_changed (ARDOUR::bounds_change);
3839 begin_reversible_command (_("trim to region"));
3842 _session->add_command(new StatefulDiffCommand (region));
3846 commit_reversible_command ();
3851 Editor::unfreeze_route ()
3853 if (clicked_routeview == 0 || !clicked_routeview->is_track()) {
3857 clicked_routeview->track()->unfreeze ();
3861 Editor::_freeze_thread (void* arg)
3863 return static_cast<Editor*>(arg)->freeze_thread ();
3867 Editor::freeze_thread ()
3869 /* create event pool because we may need to talk to the session */
3870 SessionEvent::create_per_thread_pool ("freeze events", 64);
3871 /* create per-thread buffers for process() tree to use */
3872 clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
3873 current_interthread_info->done = true;
3878 Editor::freeze_route ()
3884 /* stop transport before we start. this is important */
3886 _session->request_transport_speed (0.0);
3888 /* wait for just a little while, because the above call is asynchronous */
3890 Glib::usleep (250000);
3892 if (clicked_routeview == 0 || !clicked_routeview->is_audio_track()) {
3896 if (!clicked_routeview->track()->bounceable (clicked_routeview->track()->main_outs(), true)) {
3898 _("This track/bus cannot be frozen because the signal adds or loses channels before reaching the outputs.\n"
3899 "This is typically caused by plugins that generate stereo output from mono input or vice versa.")
3901 d.set_title (_("Cannot freeze"));
3906 if (clicked_routeview->track()->has_external_redirects()) {
3907 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"
3908 "Freezing will only process the signal as far as the first send/insert/return."),
3909 clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
3911 d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
3912 d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
3913 d.set_title (_("Freeze Limits"));
3915 int response = d.run ();
3918 case Gtk::RESPONSE_CANCEL:
3925 InterThreadInfo itt;
3926 current_interthread_info = &itt;
3928 InterthreadProgressWindow ipw (current_interthread_info, _("Freeze"), _("Cancel Freeze"));
3930 pthread_create_and_store (X_("freezer"), &itt.thread, _freeze_thread, this);
3932 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
3934 while (!itt.done && !itt.cancel) {
3935 gtk_main_iteration ();
3938 pthread_join (itt.thread, 0);
3939 current_interthread_info = 0;
3943 Editor::bounce_range_selection (bool replace, bool enable_processing)
3945 if (selection->time.empty()) {
3949 TrackSelection views = selection->tracks;
3951 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3953 if (enable_processing) {
3955 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3957 if (rtv && rtv->track() && replace && enable_processing && !rtv->track()->bounceable (rtv->track()->main_outs(), false)) {
3959 _("You can't perform this operation because the processing of the signal "
3960 "will cause one or more of the tracks to end up with a region with more channels than this track has inputs.\n\n"
3961 "You can do this without processing, which is a different operation.")
3963 d.set_title (_("Cannot bounce"));
3970 framepos_t start = selection->time[clicked_selection].start;
3971 framepos_t end = selection->time[clicked_selection].end;
3972 framepos_t cnt = end - start + 1;
3973 bool in_command = false;
3975 for (TrackViewList::iterator i = views.begin(); i != views.end(); ++i) {
3977 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
3983 boost::shared_ptr<Playlist> playlist;
3985 if ((playlist = rtv->playlist()) == 0) {
3989 InterThreadInfo itt;
3991 playlist->clear_changes ();
3992 playlist->clear_owned_changes ();
3994 boost::shared_ptr<Region> r;
3996 if (enable_processing) {
3997 r = rtv->track()->bounce_range (start, start+cnt, itt, rtv->track()->main_outs(), false);
3999 r = rtv->track()->bounce_range (start, start+cnt, itt, boost::shared_ptr<Processor>(), false);
4007 list<AudioRange> ranges;
4008 ranges.push_back (AudioRange (start, start+cnt, 0));
4009 playlist->cut (ranges); // discard result
4010 playlist->add_region (r, start);
4014 begin_reversible_command (_("bounce range"));
4017 vector<Command*> cmds;
4018 playlist->rdiff (cmds);
4019 _session->add_commands (cmds);
4021 _session->add_command (new StatefulDiffCommand (playlist));
4025 commit_reversible_command ();
4029 /** Delete selected regions, automation points or a time range */
4033 //special case: if the user is pointing in the editor/mixer strip, they may be trying to delete a plugin.
4034 //we need this because the editor-mixer strip is in the editor window, so it doesn't get the bindings from the mix window
4035 bool deleted = false;
4036 if ( current_mixer_strip && current_mixer_strip == MixerStrip::entered_mixer_strip() )
4037 deleted = current_mixer_strip->delete_processors ();
4043 /** Cut selected regions, automation points or a time range */
4050 /** Copy selected regions, automation points or a time range */
4058 /** @return true if a Cut, Copy or Clear is possible */
4060 Editor::can_cut_copy () const
4062 if (!selection->time.empty() || !selection->regions.empty() || !selection->points.empty())
4069 /** Cut, copy or clear selected regions, automation points or a time range.
4070 * @param op Operation (Delete, Cut, Copy or Clear)
4073 Editor::cut_copy (CutCopyOp op)
4075 /* only cancel selection if cut/copy is successful.*/
4081 opname = _("delete");
4090 opname = _("clear");
4094 /* if we're deleting something, and the mouse is still pressed,
4095 the thing we started a drag for will be gone when we release
4096 the mouse button(s). avoid this. see part 2 at the end of
4100 if (op == Delete || op == Cut || op == Clear) {
4101 if (_drags->active ()) {
4106 if ( op != Delete ) //"Delete" doesn't change copy/paste buf
4107 cut_buffer->clear ();
4109 if (entered_marker) {
4111 /* cut/delete op while pointing at a marker */
4114 Location* loc = find_location_from_marker (entered_marker, ignored);
4116 if (_session && loc) {
4117 entered_marker = NULL;
4118 Glib::signal_idle().connect (sigc::bind (sigc::mem_fun(*this, &Editor::really_remove_marker), loc));
4125 switch (mouse_mode) {
4128 begin_reversible_command (opname + ' ' + X_("MIDI"));
4130 commit_reversible_command ();
4136 bool did_edit = false;
4138 if (!selection->regions.empty() || !selection->points.empty()) {
4139 begin_reversible_command (opname + ' ' + _("objects"));
4142 if (!selection->regions.empty()) {
4143 cut_copy_regions (op, selection->regions);
4145 if (op == Cut || op == Delete) {
4146 selection->clear_regions ();
4150 if (!selection->points.empty()) {
4151 cut_copy_points (op);
4153 if (op == Cut || op == Delete) {
4154 selection->clear_points ();
4157 } else if (selection->time.empty()) {
4158 framepos_t start, end;
4159 /* no time selection, see if we can get an edit range
4162 if (get_edit_op_range (start, end)) {
4163 selection->set (start, end);
4165 } else if (!selection->time.empty()) {
4166 begin_reversible_command (opname + ' ' + _("range"));
4169 cut_copy_ranges (op);
4171 if (op == Cut || op == Delete) {
4172 selection->clear_time ();
4177 /* reset repeated paste state */
4180 commit_reversible_command ();
4183 if (op == Delete || op == Cut || op == Clear) {
4189 struct AutomationRecord {
4190 AutomationRecord () : state (0) , line(NULL) {}
4191 AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
4193 XMLNode* state; ///< state before any operation
4194 const AutomationLine* line; ///< line this came from
4195 boost::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
4197 struct PointsSelectionPositionSorter {
4198 bool operator() (ControlPoint* a, ControlPoint* b) {
4199 return (*(a->model()))->when < (*(b->model()))->when;
4202 /** Cut, copy or clear selected automation points.
4203 * @param op Operation (Cut, Copy or Clear)
4206 Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool midi)
4208 if (selection->points.empty ()) {
4212 /* XXX: not ideal, as there may be more than one track involved in the point selection */
4213 _last_cut_copy_source_track = &selection->points.front()->line().trackview;
4215 /* Keep a record of the AutomationLists that we end up using in this operation */
4216 typedef std::map<boost::shared_ptr<AutomationList>, AutomationRecord> Lists;
4219 /* user could select points in any order */
4220 selection->points.sort(PointsSelectionPositionSorter ());
4222 /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
4223 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4224 const AutomationLine& line = (*sel_point)->line();
4225 const boost::shared_ptr<AutomationList> al = line.the_list();
4226 if (lists.find (al) == lists.end ()) {
4227 /* We haven't seen this list yet, so make a record for it. This includes
4228 taking a copy of its current state, in case this is needed for undo later.
4230 lists[al] = AutomationRecord (&al->get_state (), &line);
4234 if (op == Cut || op == Copy) {
4235 /* This operation will involve putting things in the cut buffer, so create an empty
4236 ControlList for each of our source lists to put the cut buffer data in.
4238 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4239 i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor());
4242 /* Add all selected points to the relevant copy ControlLists */
4243 framepos_t start = std::numeric_limits<framepos_t>::max();
4244 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4245 boost::shared_ptr<AutomationList> al = (*sel_point)->line().the_list();
4246 AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
4248 lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
4250 /* Update earliest MIDI start time in beats */
4251 earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
4253 /* Update earliest session start time in frames */
4254 start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
4258 /* Snap start time backwards, so copy/paste is snap aligned. */
4260 if (earliest == Evoral::Beats::max()) {
4261 earliest = Evoral::Beats(); // Weird... don't offset
4263 earliest.round_down_to_beat();
4265 if (start == std::numeric_limits<double>::max()) {
4266 start = 0; // Weird... don't offset
4268 snap_to(start, RoundDownMaybe);
4271 const double line_offset = midi ? earliest.to_double() : start;
4272 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4273 /* Correct this copy list so that it is relative to the earliest
4274 start time, so relative ordering between points is preserved
4275 when copying from several lists and the paste starts at the
4276 earliest copied piece of data. */
4277 boost::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
4278 for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
4279 (*ctrl_evt)->when -= line_offset;
4282 /* And add it to the cut buffer */
4283 cut_buffer->add (al_cpy);
4287 if (op == Delete || op == Cut) {
4288 /* This operation needs to remove things from the main AutomationList, so do that now */
4290 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4291 i->first->freeze ();
4294 /* Remove each selected point from its AutomationList */
4295 for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
4296 AutomationLine& line = (*sel_point)->line ();
4297 boost::shared_ptr<AutomationList> al = line.the_list();
4301 if (dynamic_cast<AudioRegionGainLine*> (&line)) {
4302 /* removing of first and last gain point in region gain lines is prohibited*/
4303 if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
4309 al->erase ((*sel_point)->model ());
4313 /* Thaw the lists and add undo records for them */
4314 for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
4315 boost::shared_ptr<AutomationList> al = i->first;
4317 _session->add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
4322 /** Cut, copy or clear selected automation points.
4323 * @param op Operation (Cut, Copy or Clear)
4326 Editor::cut_copy_midi (CutCopyOp op)
4328 Evoral::Beats earliest = Evoral::Beats::max();
4329 for (MidiRegionSelection::iterator i = selection->midi_regions.begin(); i != selection->midi_regions.end(); ++i) {
4330 MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(*i);
4332 if (!mrv->selection().empty()) {
4333 earliest = std::min(earliest, (*mrv->selection().begin())->note()->time());
4335 mrv->cut_copy_clear (op);
4337 /* XXX: not ideal, as there may be more than one track involved in the selection */
4338 _last_cut_copy_source_track = &mrv->get_time_axis_view();
4342 if (!selection->points.empty()) {
4343 cut_copy_points (op, earliest, true);
4344 if (op == Cut || op == Delete) {
4345 selection->clear_points ();
4350 struct lt_playlist {
4351 bool operator () (const PlaylistState& a, const PlaylistState& b) {
4352 return a.playlist < b.playlist;
4356 struct PlaylistMapping {
4358 boost::shared_ptr<Playlist> pl;
4360 PlaylistMapping (TimeAxisView* tvp) : tv (tvp) {}
4363 /** Remove `clicked_regionview' */
4365 Editor::remove_clicked_region ()
4367 if (clicked_routeview == 0 || clicked_regionview == 0) {
4371 begin_reversible_command (_("remove region"));
4373 boost::shared_ptr<Playlist> playlist = clicked_routeview->playlist();
4375 playlist->clear_changes ();
4376 playlist->clear_owned_changes ();
4377 playlist->remove_region (clicked_regionview->region());
4378 if (Config->get_edit_mode() == Ripple)
4379 playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr<Region>());
4381 /* We might have removed regions, which alters other regions' layering_index,
4382 so we need to do a recursive diff here.
4384 vector<Command*> cmds;
4385 playlist->rdiff (cmds);
4386 _session->add_commands (cmds);
4388 _session->add_command(new StatefulDiffCommand (playlist));
4389 commit_reversible_command ();
4393 /** Remove the selected regions */
4395 Editor::remove_selected_regions ()
4397 RegionSelection rs = get_regions_from_selection_and_entered ();
4399 if (!_session || rs.empty()) {
4403 list<boost::shared_ptr<Region> > regions_to_remove;
4405 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
4406 // we can't just remove the region(s) in this loop because
4407 // this removes them from the RegionSelection, and they thus
4408 // disappear from underneath the iterator, and the ++i above
4409 // SEGVs in a puzzling fashion.
4411 // so, first iterate over the regions to be removed from rs and
4412 // add them to the regions_to_remove list, and then
4413 // iterate over the list to actually remove them.
4415 regions_to_remove.push_back ((*i)->region());
4418 vector<boost::shared_ptr<Playlist> > playlists;
4420 for (list<boost::shared_ptr<Region> >::iterator rl = regions_to_remove.begin(); rl != regions_to_remove.end(); ++rl) {
4422 boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
4425 // is this check necessary?
4429 /* get_regions_from_selection_and_entered() guarantees that
4430 the playlists involved are unique, so there is no need
4434 playlists.push_back (playlist);
4436 playlist->clear_changes ();
4437 playlist->clear_owned_changes ();
4438 playlist->freeze ();
4439 playlist->remove_region (*rl);
4440 if (Config->get_edit_mode() == Ripple)
4441 playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr<Region>());
4445 vector<boost::shared_ptr<Playlist> >::iterator pl;
4446 bool in_command = false;
4448 for (pl = playlists.begin(); pl != playlists.end(); ++pl) {
4451 /* We might have removed regions, which alters other regions' layering_index,
4452 so we need to do a recursive diff here.
4456 begin_reversible_command (_("remove region"));
4459 vector<Command*> cmds;
4460 (*pl)->rdiff (cmds);
4461 _session->add_commands (cmds);
4463 _session->add_command(new StatefulDiffCommand (*pl));
4467 commit_reversible_command ();
4471 /** Cut, copy or clear selected regions.
4472 * @param op Operation (Cut, Copy or Clear)
4475 Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs)
4477 /* we can't use a std::map here because the ordering is important, and we can't trivially sort
4478 a map when we want ordered access to both elements. i think.
4481 vector<PlaylistMapping> pmap;
4483 framepos_t first_position = max_framepos;
4485 typedef set<boost::shared_ptr<Playlist> > FreezeList;
4486 FreezeList freezelist;
4488 /* get ordering correct before we cut/copy */
4490 rs.sort_by_position_and_track ();
4492 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
4494 first_position = min ((framepos_t) (*x)->region()->position(), first_position);
4496 if (op == Cut || op == Clear || op == Delete) {
4497 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4500 FreezeList::iterator fl;
4502 // only take state if this is a new playlist.
4503 for (fl = freezelist.begin(); fl != freezelist.end(); ++fl) {
4509 if (fl == freezelist.end()) {
4510 pl->clear_changes();
4511 pl->clear_owned_changes ();
4513 freezelist.insert (pl);
4518 TimeAxisView* tv = &(*x)->get_time_axis_view();
4519 vector<PlaylistMapping>::iterator z;
4521 for (z = pmap.begin(); z != pmap.end(); ++z) {
4522 if ((*z).tv == tv) {
4527 if (z == pmap.end()) {
4528 pmap.push_back (PlaylistMapping (tv));
4532 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ) {
4534 boost::shared_ptr<Playlist> pl = (*x)->region()->playlist();
4537 /* region not yet associated with a playlist (e.g. unfinished
4544 TimeAxisView& tv = (*x)->get_time_axis_view();
4545 boost::shared_ptr<Playlist> npl;
4546 RegionSelection::iterator tmp;
4553 vector<PlaylistMapping>::iterator z;
4555 for (z = pmap.begin(); z != pmap.end(); ++z) {
4556 if ((*z).tv == &tv) {
4561 assert (z != pmap.end());
4564 npl = PlaylistFactory::create (pl->data_type(), *_session, "cutlist", true);
4572 boost::shared_ptr<Region> r = (*x)->region();
4573 boost::shared_ptr<Region> _xx;
4579 pl->remove_region (r);
4580 if (Config->get_edit_mode() == Ripple)
4581 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4585 _xx = RegionFactory::create (r);
4586 npl->add_region (_xx, r->position() - first_position);
4587 pl->remove_region (r);
4588 if (Config->get_edit_mode() == Ripple)
4589 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4593 /* copy region before adding, so we're not putting same object into two different playlists */
4594 npl->add_region (RegionFactory::create (r), r->position() - first_position);
4598 pl->remove_region (r);
4599 if (Config->get_edit_mode() == Ripple)
4600 pl->ripple (r->position(), -r->length(), boost::shared_ptr<Region>());
4609 list<boost::shared_ptr<Playlist> > foo;
4611 /* the pmap is in the same order as the tracks in which selected regions occurred */
4613 for (vector<PlaylistMapping>::iterator i = pmap.begin(); i != pmap.end(); ++i) {
4616 foo.push_back ((*i).pl);
4621 cut_buffer->set (foo);
4625 _last_cut_copy_source_track = 0;
4627 _last_cut_copy_source_track = pmap.front().tv;
4631 for (FreezeList::iterator pl = freezelist.begin(); pl != freezelist.end(); ++pl) {
4634 /* We might have removed regions, which alters other regions' layering_index,
4635 so we need to do a recursive diff here.
4637 vector<Command*> cmds;
4638 (*pl)->rdiff (cmds);
4639 _session->add_commands (cmds);
4641 _session->add_command (new StatefulDiffCommand (*pl));
4646 Editor::cut_copy_ranges (CutCopyOp op)
4648 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4650 /* Sort the track selection now, so that it if is used, the playlists
4651 selected by the calls below to cut_copy_clear are in the order that
4652 their tracks appear in the editor. This makes things like paste
4653 of ranges work properly.
4656 sort_track_selection (ts);
4659 if (!entered_track) {
4662 ts.push_back (entered_track);
4665 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4666 (*i)->cut_copy_clear (*selection, op);
4671 Editor::paste (float times, bool from_context)
4673 DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
4675 paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
4679 Editor::mouse_paste ()
4684 if (!mouse_frame (where, ignored)) {
4689 paste_internal (where, 1, get_grid_music_divisions (0));
4693 Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
4695 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position));
4697 if (cut_buffer->empty(internal_editing())) {
4701 if (position == max_framepos) {
4702 position = get_preferred_edit_position();
4703 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
4706 if (position == last_paste_pos) {
4707 /* repeated paste in the same position */
4710 /* paste in new location, reset repeated paste state */
4712 last_paste_pos = position;
4715 /* get everything in the correct order */
4718 if (!selection->tracks.empty()) {
4719 /* If there is a track selection, paste into exactly those tracks and
4720 only those tracks. This allows the user to be explicit and override
4721 the below "do the reasonable thing" logic. */
4722 ts = selection->tracks.filter_to_unique_playlists ();
4723 sort_track_selection (ts);
4725 /* Figure out which track to base the paste at. */
4726 TimeAxisView* base_track = NULL;
4727 if (_edit_point == Editing::EditAtMouse && entered_track) {
4728 /* With the mouse edit point, paste onto the track under the mouse. */
4729 base_track = entered_track;
4730 } else if (_edit_point == Editing::EditAtMouse && entered_regionview) {
4731 /* With the mouse edit point, paste onto the track of the region under the mouse. */
4732 base_track = &entered_regionview->get_time_axis_view();
4733 } else if (_last_cut_copy_source_track) {
4734 /* Paste to the track that the cut/copy came from (see mantis #333). */
4735 base_track = _last_cut_copy_source_track;
4737 /* This is "impossible" since we've copied... well, do nothing. */
4741 /* Walk up to parent if necessary, so base track is a route. */
4742 while (base_track->get_parent()) {
4743 base_track = base_track->get_parent();
4746 /* Add base track and all tracks below it. The paste logic will select
4747 the appropriate object types from the cut buffer in relative order. */
4748 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4749 if ((*i)->order() >= base_track->order()) {
4754 /* Sort tracks so the nth track of type T will pick the nth object of type T. */
4755 sort_track_selection (ts);
4757 /* Add automation children of each track in order, for pasting several lines. */
4758 for (TrackViewList::iterator i = ts.begin(); i != ts.end();) {
4759 /* Add any automation children for pasting several lines */
4760 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*i++);
4765 typedef RouteTimeAxisView::AutomationTracks ATracks;
4766 const ATracks& atracks = rtv->automation_tracks();
4767 for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
4768 i = ts.insert(i, a->second.get());
4773 /* We now have a list of trackviews starting at base_track, including
4774 automation children, in the order shown in the editor, e.g. R1,
4775 R1.A1, R1.A2, R2, R2.A1, ... */
4778 begin_reversible_command (Operations::paste);
4780 if (ts.size() == 1 && cut_buffer->lines.size() == 1 &&
4781 dynamic_cast<AutomationTimeAxisView*>(ts.front())) {
4782 /* Only one line copied, and one automation track selected. Do a
4783 "greedy" paste from one automation type to another. */
4785 PasteContext ctx(paste_count, times, ItemCounts(), true);
4786 ts.front()->paste (position, *cut_buffer, ctx, sub_num);
4790 /* Paste into tracks */
4792 PasteContext ctx(paste_count, times, ItemCounts(), false);
4793 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4794 (*i)->paste (position, *cut_buffer, ctx, sub_num);
4798 commit_reversible_command ();
4802 Editor::duplicate_regions (float times)
4804 RegionSelection rs (get_regions_from_selection_and_entered());
4805 duplicate_some_regions (rs, times);
4809 Editor::duplicate_some_regions (RegionSelection& regions, float times)
4811 if (regions.empty ()) {
4815 boost::shared_ptr<Playlist> playlist;
4816 RegionSelection sel = regions; // clear (below) may clear the argument list if its the current region selection
4817 RegionSelection foo;
4819 framepos_t const start_frame = regions.start ();
4820 framepos_t const end_frame = regions.end_frame ();
4821 framecnt_t const gap = end_frame - start_frame + 1;
4823 begin_reversible_command (Operations::duplicate_region);
4825 selection->clear_regions ();
4827 for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
4829 boost::shared_ptr<Region> r ((*i)->region());
4831 TimeAxisView& tv = (*i)->get_time_axis_view();
4832 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4833 latest_regionviews.clear ();
4834 sigc::connection c = rtv->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
4836 framepos_t const position = end_frame + (r->first_frame() - start_frame + 1);
4837 playlist = (*i)->region()->playlist();
4838 playlist->clear_changes ();
4839 playlist->duplicate (r, position, gap, times);
4840 _session->add_command(new StatefulDiffCommand (playlist));
4844 foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
4848 selection->set (foo);
4851 commit_reversible_command ();
4855 Editor::duplicate_selection (float times)
4857 if (selection->time.empty() || selection->tracks.empty()) {
4861 boost::shared_ptr<Playlist> playlist;
4863 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4865 bool in_command = false;
4867 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4868 if ((playlist = (*i)->playlist()) == 0) {
4871 playlist->clear_changes ();
4873 if (clicked_selection) {
4874 playlist->duplicate_range (selection->time[clicked_selection], times);
4876 playlist->duplicate_ranges (selection->time, times);
4880 begin_reversible_command (_("duplicate range selection"));
4883 _session->add_command (new StatefulDiffCommand (playlist));
4888 if (times == 1.0f) {
4889 // now "move" range selection to after the current range selection
4890 framecnt_t distance = 0;
4892 if (clicked_selection) {
4894 selection->time[clicked_selection].end - selection->time[clicked_selection].start;
4896 distance = selection->time.end_frame () - selection->time.start ();
4899 selection->move_time (distance);
4901 commit_reversible_command ();
4905 /** Reset all selected points to the relevant default value */
4907 Editor::reset_point_selection ()
4909 for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
4910 ARDOUR::AutomationList::iterator j = (*i)->model ();
4911 (*j)->value = (*i)->line().the_list()->default_value ();
4916 Editor::center_playhead ()
4918 float const page = _visible_canvas_width * samples_per_pixel;
4919 center_screen_internal (playhead_cursor->current_frame (), page);
4923 Editor::center_edit_point ()
4925 float const page = _visible_canvas_width * samples_per_pixel;
4926 center_screen_internal (get_preferred_edit_position(), page);
4929 /** Caller must begin and commit a reversible command */
4931 Editor::clear_playlist (boost::shared_ptr<Playlist> playlist)
4933 playlist->clear_changes ();
4935 _session->add_command (new StatefulDiffCommand (playlist));
4939 Editor::nudge_track (bool use_edit, bool forwards)
4941 boost::shared_ptr<Playlist> playlist;
4942 framepos_t distance;
4943 framepos_t next_distance;
4947 start = get_preferred_edit_position();
4952 if ((distance = get_nudge_distance (start, next_distance)) == 0) {
4956 if (selection->tracks.empty()) {
4960 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
4961 bool in_command = false;
4963 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
4965 if ((playlist = (*i)->playlist()) == 0) {
4969 playlist->clear_changes ();
4970 playlist->clear_owned_changes ();
4972 playlist->nudge_after (start, distance, forwards);
4975 begin_reversible_command (_("nudge track"));
4978 vector<Command*> cmds;
4980 playlist->rdiff (cmds);
4981 _session->add_commands (cmds);
4983 _session->add_command (new StatefulDiffCommand (playlist));
4987 commit_reversible_command ();
4992 Editor::remove_last_capture ()
4994 vector<string> choices;
5001 if (Config->get_verify_remove_last_capture()) {
5002 prompt = _("Do you really want to destroy the last capture?"
5003 "\n(This is destructive and cannot be undone)");
5005 choices.push_back (_("No, do nothing."));
5006 choices.push_back (_("Yes, destroy it."));
5008 Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
5010 if (prompter.run () == 1) {
5011 _session->remove_last_capture ();
5012 _regions->redisplay ();
5016 _session->remove_last_capture();
5017 _regions->redisplay ();
5022 Editor::normalize_region ()
5028 RegionSelection rs = get_regions_from_selection_and_entered ();
5034 NormalizeDialog dialog (rs.size() > 1);
5036 if (dialog.run () != RESPONSE_ACCEPT) {
5040 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5043 /* XXX: should really only count audio regions here */
5044 int const regions = rs.size ();
5046 /* Make a list of the selected audio regions' maximum amplitudes, and also
5047 obtain the maximum amplitude of them all.
5049 list<double> max_amps;
5050 list<double> rms_vals;
5053 bool use_rms = dialog.constrain_rms ();
5055 for (RegionSelection::const_iterator i = rs.begin(); i != rs.end(); ++i) {
5056 AudioRegionView const * arv = dynamic_cast<AudioRegionView const *> (*i);
5060 dialog.descend (1.0 / regions);
5061 double const a = arv->audio_region()->maximum_amplitude (&dialog);
5063 double r = arv->audio_region()->rms (&dialog);
5064 max_rms = max (max_rms, r);
5065 rms_vals.push_back (r);
5069 /* the user cancelled the operation */
5073 max_amps.push_back (a);
5074 max_amp = max (max_amp, a);
5078 list<double>::const_iterator a = max_amps.begin ();
5079 list<double>::const_iterator l = rms_vals.begin ();
5080 bool in_command = false;
5082 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5083 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*r);
5088 arv->region()->clear_changes ();
5090 double amp = dialog.normalize_individually() ? *a : max_amp;
5091 double target = dialog.target_peak (); // dB
5094 double const amp_rms = dialog.normalize_individually() ? *l : max_rms;
5095 const double t_rms = dialog.target_rms ();
5096 const gain_t c_peak = dB_to_coefficient (target);
5097 const gain_t c_rms = dB_to_coefficient (t_rms);
5098 if ((amp_rms / c_rms) > (amp / c_peak)) {
5104 arv->audio_region()->normalize (amp, target);
5107 begin_reversible_command (_("normalize"));
5110 _session->add_command (new StatefulDiffCommand (arv->region()));
5117 commit_reversible_command ();
5123 Editor::reset_region_scale_amplitude ()
5129 RegionSelection rs = get_regions_from_selection_and_entered ();
5135 bool in_command = false;
5137 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5138 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5141 arv->region()->clear_changes ();
5142 arv->audio_region()->set_scale_amplitude (1.0f);
5145 begin_reversible_command ("reset gain");
5148 _session->add_command (new StatefulDiffCommand (arv->region()));
5152 commit_reversible_command ();
5157 Editor::adjust_region_gain (bool up)
5159 RegionSelection rs = get_regions_from_selection_and_entered ();
5161 if (!_session || rs.empty()) {
5165 bool in_command = false;
5167 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
5168 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5173 arv->region()->clear_changes ();
5175 double dB = accurate_coefficient_to_dB (arv->audio_region()->scale_amplitude ());
5183 arv->audio_region()->set_scale_amplitude (dB_to_coefficient (dB));
5186 begin_reversible_command ("adjust region gain");
5189 _session->add_command (new StatefulDiffCommand (arv->region()));
5193 commit_reversible_command ();
5199 Editor::reverse_region ()
5205 Reverse rev (*_session);
5206 apply_filter (rev, _("reverse regions"));
5210 Editor::strip_region_silence ()
5216 RegionSelection rs = get_regions_from_selection_and_entered ();
5222 std::list<RegionView*> audio_only;
5224 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5225 AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
5227 audio_only.push_back (arv);
5231 assert (!audio_only.empty());
5233 StripSilenceDialog d (_session, audio_only);
5234 int const r = d.run ();
5238 if (r == Gtk::RESPONSE_OK) {
5239 ARDOUR::AudioIntervalMap silences;
5240 d.silences (silences);
5241 StripSilence s (*_session, silences, d.fade_length());
5243 apply_filter (s, _("strip silence"), &d);
5248 Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
5250 Evoral::Sequence<Evoral::Beats>::Notes selected;
5251 mrv.selection_as_notelist (selected, true);
5253 vector<Evoral::Sequence<Evoral::Beats>::Notes> v;
5254 v.push_back (selected);
5256 Evoral::Beats pos_beats = Evoral::Beats (mrv.midi_region()->beat()) - mrv.midi_region()->start_beats();
5258 return op (mrv.midi_region()->model(), pos_beats, v);
5262 Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
5268 bool in_command = false;
5270 for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) {
5271 RegionSelection::const_iterator tmp = r;
5274 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*r);
5277 Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv);
5280 begin_reversible_command (op.name ());
5284 _session->add_command (cmd);
5292 commit_reversible_command ();
5297 Editor::fork_region ()
5299 RegionSelection rs = get_regions_from_selection_and_entered ();
5305 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5306 bool in_command = false;
5310 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5311 RegionSelection::iterator tmp = r;
5314 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*>(*r);
5318 boost::shared_ptr<Playlist> playlist = mrv->region()->playlist();
5319 boost::shared_ptr<MidiSource> new_source = _session->create_midi_source_by_stealing_name (mrv->midi_view()->track());
5320 boost::shared_ptr<MidiRegion> newregion = mrv->midi_region()->clone (new_source);
5323 begin_reversible_command (_("Fork Region(s)"));
5326 playlist->clear_changes ();
5327 playlist->replace_region (mrv->region(), newregion, mrv->region()->position());
5328 _session->add_command(new StatefulDiffCommand (playlist));
5330 error << string_compose (_("Could not unlink %1"), mrv->region()->name()) << endmsg;
5338 commit_reversible_command ();
5343 Editor::quantize_region ()
5346 quantize_regions(get_regions_from_selection_and_entered ());
5351 Editor::quantize_regions (const RegionSelection& rs)
5353 if (rs.n_midi_regions() == 0) {
5357 if (!quantize_dialog) {
5358 quantize_dialog = new QuantizeDialog (*this);
5361 if (quantize_dialog->is_mapped()) {
5362 /* in progress already */
5366 quantize_dialog->present ();
5367 const int r = quantize_dialog->run ();
5368 quantize_dialog->hide ();
5370 if (r == Gtk::RESPONSE_OK) {
5371 Quantize quant (quantize_dialog->snap_start(),
5372 quantize_dialog->snap_end(),
5373 quantize_dialog->start_grid_size(),
5374 quantize_dialog->end_grid_size(),
5375 quantize_dialog->strength(),
5376 quantize_dialog->swing(),
5377 quantize_dialog->threshold());
5379 apply_midi_note_edit_op (quant, rs);
5384 Editor::legatize_region (bool shrink_only)
5387 legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
5392 Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
5394 if (rs.n_midi_regions() == 0) {
5398 Legatize legatize(shrink_only);
5399 apply_midi_note_edit_op (legatize, rs);
5403 Editor::transform_region ()
5406 transform_regions(get_regions_from_selection_and_entered ());
5411 Editor::transform_regions (const RegionSelection& rs)
5413 if (rs.n_midi_regions() == 0) {
5420 const int r = td.run();
5423 if (r == Gtk::RESPONSE_OK) {
5424 Transform transform(td.get());
5425 apply_midi_note_edit_op(transform, rs);
5430 Editor::transpose_region ()
5433 transpose_regions(get_regions_from_selection_and_entered ());
5438 Editor::transpose_regions (const RegionSelection& rs)
5440 if (rs.n_midi_regions() == 0) {
5445 int const r = d.run ();
5447 if (r == RESPONSE_ACCEPT) {
5448 Transpose transpose(d.semitones ());
5449 apply_midi_note_edit_op (transpose, rs);
5454 Editor::insert_patch_change (bool from_context)
5456 RegionSelection rs = get_regions_from_selection_and_entered ();
5462 const framepos_t p = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context);
5464 /* XXX: bit of a hack; use the MIDNAM from the first selected region;
5465 there may be more than one, but the PatchChangeDialog can only offer
5466 one set of patch menus.
5468 MidiRegionView* first = dynamic_cast<MidiRegionView*> (rs.front ());
5470 Evoral::PatchChange<Evoral::Beats> empty (Evoral::Beats(), 0, 0, 0);
5471 PatchChangeDialog d (0, _session, empty, first->instrument_info(), Gtk::Stock::ADD);
5473 if (d.run() == RESPONSE_CANCEL) {
5477 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
5478 MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
5480 if (p >= mrv->region()->first_frame() && p <= mrv->region()->last_frame()) {
5481 mrv->add_patch_change (p - mrv->region()->position(), d.patch ());
5488 Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress)
5490 RegionSelection rs = get_regions_from_selection_and_entered ();
5496 CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
5497 bool in_command = false;
5502 int const N = rs.size ();
5504 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ) {
5505 RegionSelection::iterator tmp = r;
5508 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
5510 boost::shared_ptr<Playlist> playlist = arv->region()->playlist();
5513 progress->descend (1.0 / N);
5516 if (arv->audio_region()->apply (filter, progress) == 0) {
5518 playlist->clear_changes ();
5519 playlist->clear_owned_changes ();
5522 begin_reversible_command (command);
5526 if (filter.results.empty ()) {
5528 /* no regions returned; remove the old one */
5529 playlist->remove_region (arv->region ());
5533 std::vector<boost::shared_ptr<Region> >::iterator res = filter.results.begin ();
5535 /* first region replaces the old one */
5536 playlist->replace_region (arv->region(), *res, (*res)->position());
5540 while (res != filter.results.end()) {
5541 playlist->add_region (*res, (*res)->position());
5547 /* We might have removed regions, which alters other regions' layering_index,
5548 so we need to do a recursive diff here.
5550 vector<Command*> cmds;
5551 playlist->rdiff (cmds);
5552 _session->add_commands (cmds);
5554 _session->add_command(new StatefulDiffCommand (playlist));
5558 progress->ascend ();
5567 commit_reversible_command ();
5572 Editor::external_edit_region ()
5578 Editor::reset_region_gain_envelopes ()
5580 RegionSelection rs = get_regions_from_selection_and_entered ();
5582 if (!_session || rs.empty()) {
5586 bool in_command = false;
5588 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5589 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5591 boost::shared_ptr<AutomationList> alist (arv->audio_region()->envelope());
5592 XMLNode& before (alist->get_state());
5594 arv->audio_region()->set_default_envelope ();
5597 begin_reversible_command (_("reset region gain"));
5600 _session->add_command (new MementoCommand<AutomationList>(*arv->audio_region()->envelope().get(), &before, &alist->get_state()));
5605 commit_reversible_command ();
5610 Editor::set_region_gain_visibility (RegionView* rv)
5612 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (rv);
5614 arv->update_envelope_visibility();
5619 Editor::set_gain_envelope_visibility ()
5625 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5626 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
5628 v->audio_view()->foreach_regionview (sigc::mem_fun (this, &Editor::set_region_gain_visibility));
5634 Editor::toggle_gain_envelope_active ()
5636 if (_ignore_region_action) {
5640 RegionSelection rs = get_regions_from_selection_and_entered ();
5642 if (!_session || rs.empty()) {
5646 bool in_command = false;
5648 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5649 AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i);
5651 arv->region()->clear_changes ();
5652 arv->audio_region()->set_envelope_active (!arv->audio_region()->envelope_active());
5655 begin_reversible_command (_("region gain envelope active"));
5658 _session->add_command (new StatefulDiffCommand (arv->region()));
5663 commit_reversible_command ();
5668 Editor::toggle_region_lock ()
5670 if (_ignore_region_action) {
5674 RegionSelection rs = get_regions_from_selection_and_entered ();
5676 if (!_session || rs.empty()) {
5680 begin_reversible_command (_("toggle region lock"));
5682 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5683 (*i)->region()->clear_changes ();
5684 (*i)->region()->set_locked (!(*i)->region()->locked());
5685 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5688 commit_reversible_command ();
5692 Editor::toggle_region_video_lock ()
5694 if (_ignore_region_action) {
5698 RegionSelection rs = get_regions_from_selection_and_entered ();
5700 if (!_session || rs.empty()) {
5704 begin_reversible_command (_("Toggle Video Lock"));
5706 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5707 (*i)->region()->clear_changes ();
5708 (*i)->region()->set_video_locked (!(*i)->region()->video_locked());
5709 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5712 commit_reversible_command ();
5716 Editor::toggle_region_lock_style ()
5718 if (_ignore_region_action) {
5722 RegionSelection rs = get_regions_from_selection_and_entered ();
5724 if (!_session || rs.empty()) {
5728 begin_reversible_command (_("region lock style"));
5730 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5731 (*i)->region()->clear_changes ();
5732 PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
5733 (*i)->region()->set_position_lock_style (ns);
5734 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5737 commit_reversible_command ();
5741 Editor::toggle_opaque_region ()
5743 if (_ignore_region_action) {
5747 RegionSelection rs = get_regions_from_selection_and_entered ();
5749 if (!_session || rs.empty()) {
5753 begin_reversible_command (_("change region opacity"));
5755 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
5756 (*i)->region()->clear_changes ();
5757 (*i)->region()->set_opaque (!(*i)->region()->opaque());
5758 _session->add_command (new StatefulDiffCommand ((*i)->region()));
5761 commit_reversible_command ();
5765 Editor::toggle_record_enable ()
5767 bool new_state = false;
5769 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5770 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5773 if (!rtav->is_track())
5777 new_state = !rtav->track()->rec_enable_control()->get_value();
5781 rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup);
5786 Editor::toggle_solo ()
5788 bool new_state = false;
5790 boost::shared_ptr<ControlList> cl (new ControlList);
5792 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5793 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5800 new_state = !rtav->route()->soloed ();
5804 cl->push_back (rtav->route()->solo_control());
5807 _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
5811 Editor::toggle_mute ()
5813 bool new_state = false;
5815 boost::shared_ptr<RouteList> rl (new RouteList);
5817 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
5818 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
5825 new_state = !rtav->route()->muted();
5829 rl->push_back (rtav->route());
5832 _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
5836 Editor::toggle_solo_isolate ()
5842 Editor::fade_range ()
5844 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
5846 begin_reversible_command (_("fade range"));
5848 for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) {
5849 (*i)->fade_range (selection->time);
5852 commit_reversible_command ();
5857 Editor::set_fade_length (bool in)
5859 RegionSelection rs = get_regions_from_selection_and_entered ();
5865 /* we need a region to measure the offset from the start */
5867 RegionView* rv = rs.front ();
5869 framepos_t pos = get_preferred_edit_position();
5873 if (pos > rv->region()->last_frame() || pos < rv->region()->first_frame()) {
5874 /* edit point is outside the relevant region */
5879 if (pos <= rv->region()->position()) {
5883 len = pos - rv->region()->position();
5884 cmd = _("set fade in length");
5886 if (pos >= rv->region()->last_frame()) {
5890 len = rv->region()->last_frame() - pos;
5891 cmd = _("set fade out length");
5894 bool in_command = false;
5896 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5897 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5903 boost::shared_ptr<AutomationList> alist;
5905 alist = tmp->audio_region()->fade_in();
5907 alist = tmp->audio_region()->fade_out();
5910 XMLNode &before = alist->get_state();
5913 tmp->audio_region()->set_fade_in_length (len);
5914 tmp->audio_region()->set_fade_in_active (true);
5916 tmp->audio_region()->set_fade_out_length (len);
5917 tmp->audio_region()->set_fade_out_active (true);
5921 begin_reversible_command (cmd);
5924 XMLNode &after = alist->get_state();
5925 _session->add_command(new MementoCommand<AutomationList>(*alist, &before, &after));
5929 commit_reversible_command ();
5934 Editor::set_fade_in_shape (FadeShape shape)
5936 RegionSelection rs = get_regions_from_selection_and_entered ();
5941 bool in_command = false;
5943 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5944 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5950 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_in();
5951 XMLNode &before = alist->get_state();
5953 tmp->audio_region()->set_fade_in_shape (shape);
5956 begin_reversible_command (_("set fade in shape"));
5959 XMLNode &after = alist->get_state();
5960 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5964 commit_reversible_command ();
5969 Editor::set_fade_out_shape (FadeShape shape)
5971 RegionSelection rs = get_regions_from_selection_and_entered ();
5976 bool in_command = false;
5978 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
5979 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
5985 boost::shared_ptr<AutomationList> alist = tmp->audio_region()->fade_out();
5986 XMLNode &before = alist->get_state();
5988 tmp->audio_region()->set_fade_out_shape (shape);
5991 begin_reversible_command (_("set fade out shape"));
5994 XMLNode &after = alist->get_state();
5995 _session->add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &after));
5999 commit_reversible_command ();
6004 Editor::set_fade_in_active (bool yn)
6006 RegionSelection rs = get_regions_from_selection_and_entered ();
6011 bool in_command = false;
6013 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6014 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6021 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6023 ar->clear_changes ();
6024 ar->set_fade_in_active (yn);
6027 begin_reversible_command (_("set fade in active"));
6030 _session->add_command (new StatefulDiffCommand (ar));
6034 commit_reversible_command ();
6039 Editor::set_fade_out_active (bool yn)
6041 RegionSelection rs = get_regions_from_selection_and_entered ();
6046 bool in_command = false;
6048 for (RegionSelection::iterator x = rs.begin(); x != rs.end(); ++x) {
6049 AudioRegionView* tmp = dynamic_cast<AudioRegionView*> (*x);
6055 boost::shared_ptr<AudioRegion> ar (tmp->audio_region());
6057 ar->clear_changes ();
6058 ar->set_fade_out_active (yn);
6061 begin_reversible_command (_("set fade out active"));
6064 _session->add_command(new StatefulDiffCommand (ar));
6068 commit_reversible_command ();
6073 Editor::toggle_region_fades (int dir)
6075 if (_ignore_region_action) {
6079 boost::shared_ptr<AudioRegion> ar;
6082 RegionSelection rs = get_regions_from_selection_and_entered ();
6088 RegionSelection::iterator i;
6089 for (i = rs.begin(); i != rs.end(); ++i) {
6090 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) != 0) {
6092 yn = ar->fade_out_active ();
6094 yn = ar->fade_in_active ();
6100 if (i == rs.end()) {
6104 /* XXX should this undo-able? */
6105 bool in_command = false;
6107 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6108 if ((ar = boost::dynamic_pointer_cast<AudioRegion>((*i)->region())) == 0) {
6111 ar->clear_changes ();
6113 if (dir == 1 || dir == 0) {
6114 ar->set_fade_in_active (!yn);
6117 if (dir == -1 || dir == 0) {
6118 ar->set_fade_out_active (!yn);
6121 begin_reversible_command (_("toggle fade active"));
6124 _session->add_command(new StatefulDiffCommand (ar));
6128 commit_reversible_command ();
6133 /** Update region fade visibility after its configuration has been changed */
6135 Editor::update_region_fade_visibility ()
6137 bool _fade_visibility = _session->config.get_show_region_fades ();
6139 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6140 AudioTimeAxisView* v = dynamic_cast<AudioTimeAxisView*>(*i);
6142 if (_fade_visibility) {
6143 v->audio_view()->show_all_fades ();
6145 v->audio_view()->hide_all_fades ();
6152 Editor::set_edit_point ()
6157 if (!mouse_frame (where, ignored)) {
6163 if (selection->markers.empty()) {
6165 mouse_add_new_marker (where);
6170 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
6173 loc->move_to (where);
6179 Editor::set_playhead_cursor ()
6181 if (entered_marker) {
6182 _session->request_locate (entered_marker->position(), _session->transport_rolling());
6187 if (!mouse_frame (where, ignored)) {
6194 _session->request_locate (where, _session->transport_rolling());
6198 if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
6199 cancel_time_selection();
6204 Editor::split_region ()
6206 if (_drags->active ()) {
6210 //if a range is selected, separate it
6211 if ( !selection->time.empty()) {
6212 separate_regions_between (selection->time);
6216 //if no range was selected, try to find some regions to split
6217 if (current_mouse_mode() == MouseObject) { //don't try this for Internal Edit, Stretch, Draw, etc.
6219 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6221 framepos_t where = get_preferred_edit_position ();
6227 if (snap_musical()) {
6228 split_regions_at (where, rs, get_grid_music_divisions (0));
6230 split_regions_at (where, rs, 0);
6236 Editor::select_next_route()
6238 if (selection->tracks.empty()) {
6239 selection->set (track_views.front());
6243 TimeAxisView* current = selection->tracks.front();
6247 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
6249 if (*i == current) {
6251 if (i != track_views.end()) {
6254 current = (*(track_views.begin()));
6255 //selection->set (*(track_views.begin()));
6261 rui = dynamic_cast<RouteUI *>(current);
6263 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6265 selection->set (current);
6267 ensure_time_axis_view_is_visible (*current, false);
6271 Editor::select_prev_route()
6273 if (selection->tracks.empty()) {
6274 selection->set (track_views.front());
6278 TimeAxisView* current = selection->tracks.front();
6282 for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
6284 if (*i == current) {
6286 if (i != track_views.rend()) {
6289 current = *(track_views.rbegin());
6294 rui = dynamic_cast<RouteUI *>(current);
6296 } while (current->hidden() || (rui == NULL) || !rui->route()->active());
6298 selection->set (current);
6300 ensure_time_axis_view_is_visible (*current, false);
6304 Editor::set_loop_from_selection (bool play)
6306 if (_session == 0) {
6310 framepos_t start, end;
6311 if (!get_selection_extents ( start, end))
6314 set_loop_range (start, end, _("set loop range from selection"));
6317 _session->request_play_loop (true, true);
6322 Editor::set_loop_from_region (bool play)
6324 framepos_t start, end;
6325 if (!get_selection_extents ( start, end))
6328 set_loop_range (start, end, _("set loop range from region"));
6331 _session->request_locate (start, true);
6332 _session->request_play_loop (true);
6337 Editor::set_punch_from_selection ()
6339 if (_session == 0) {
6343 framepos_t start, end;
6344 if (!get_selection_extents ( start, end))
6347 set_punch_range (start, end, _("set punch range from selection"));
6351 Editor::set_auto_punch_range ()
6353 // auto punch in/out button from a single button
6354 // If Punch In is unset, set punch range from playhead to end, enable punch in
6355 // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
6356 // rewound beyond the Punch In marker, in which case that marker will be moved back
6357 // to the current playhead position.
6358 // If punch out is set, it clears the punch range and Punch In/Out buttons
6360 if (_session == 0) {
6364 Location* tpl = transport_punch_location();
6365 framepos_t now = playhead_cursor->current_frame();
6366 framepos_t begin = now;
6367 framepos_t end = _session->current_end_frame();
6369 if (!_session->config.get_punch_in()) {
6370 // First Press - set punch in and create range from here to eternity
6371 set_punch_range (begin, end, _("Auto Punch In"));
6372 _session->config.set_punch_in(true);
6373 } else if (tpl && !_session->config.get_punch_out()) {
6374 // Second press - update end range marker and set punch_out
6375 if (now < tpl->start()) {
6376 // playhead has been rewound - move start back and pretend nothing happened
6378 set_punch_range (begin, end, _("Auto Punch In/Out"));
6380 // normal case for 2nd press - set the punch out
6381 end = playhead_cursor->current_frame ();
6382 set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
6383 _session->config.set_punch_out(true);
6386 if (_session->config.get_punch_out()) {
6387 _session->config.set_punch_out(false);
6390 if (_session->config.get_punch_in()) {
6391 _session->config.set_punch_in(false);
6396 // third press - unset punch in/out and remove range
6397 _session->locations()->remove(tpl);
6404 Editor::set_session_extents_from_selection ()
6406 if (_session == 0) {
6410 framepos_t start, end;
6411 if (!get_selection_extents ( start, end))
6415 if ((loc = _session->locations()->session_range_location()) == 0) {
6416 _session->set_session_extents (start, end); // this will create a new session range; no need for UNDO
6418 XMLNode &before = loc->get_state();
6420 _session->set_session_extents (start, end);
6422 XMLNode &after = loc->get_state();
6424 begin_reversible_command (_("set session start/end from selection"));
6426 _session->add_command (new MementoCommand<Location>(*loc, &before, &after));
6428 commit_reversible_command ();
6431 _session->set_end_is_free (false);
6435 Editor::set_punch_start_from_edit_point ()
6439 framepos_t start = 0;
6440 framepos_t end = max_framepos;
6442 //use the existing punch end, if any
6443 Location* tpl = transport_punch_location();
6448 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6449 start = _session->audible_frame();
6451 start = get_preferred_edit_position();
6454 //snap the selection start/end
6457 //if there's not already a sensible selection endpoint, go "forever"
6458 if ( start > end ) {
6462 set_punch_range (start, end, _("set punch start from EP"));
6468 Editor::set_punch_end_from_edit_point ()
6472 framepos_t start = 0;
6473 framepos_t end = max_framepos;
6475 //use the existing punch start, if any
6476 Location* tpl = transport_punch_location();
6478 start = tpl->start();
6481 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6482 end = _session->audible_frame();
6484 end = get_preferred_edit_position();
6487 //snap the selection start/end
6490 set_punch_range (start, end, _("set punch end from EP"));
6496 Editor::set_loop_start_from_edit_point ()
6500 framepos_t start = 0;
6501 framepos_t end = max_framepos;
6503 //use the existing loop end, if any
6504 Location* tpl = transport_loop_location();
6509 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6510 start = _session->audible_frame();
6512 start = get_preferred_edit_position();
6515 //snap the selection start/end
6518 //if there's not already a sensible selection endpoint, go "forever"
6519 if ( start > end ) {
6523 set_loop_range (start, end, _("set loop start from EP"));
6529 Editor::set_loop_end_from_edit_point ()
6533 framepos_t start = 0;
6534 framepos_t end = max_framepos;
6536 //use the existing loop start, if any
6537 Location* tpl = transport_loop_location();
6539 start = tpl->start();
6542 if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
6543 end = _session->audible_frame();
6545 end = get_preferred_edit_position();
6548 //snap the selection start/end
6551 set_loop_range (start, end, _("set loop end from EP"));
6556 Editor::set_punch_from_region ()
6558 framepos_t start, end;
6559 if (!get_selection_extents ( start, end))
6562 set_punch_range (start, end, _("set punch range from region"));
6566 Editor::pitch_shift_region ()
6568 RegionSelection rs = get_regions_from_selection_and_entered ();
6570 RegionSelection audio_rs;
6571 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
6572 if (dynamic_cast<AudioRegionView*> (*i)) {
6573 audio_rs.push_back (*i);
6577 if (audio_rs.empty()) {
6581 pitch_shift (audio_rs, 1.2);
6585 Editor::set_tempo_from_region ()
6587 RegionSelection rs = get_regions_from_selection_and_entered ();
6589 if (!_session || rs.empty()) {
6593 RegionView* rv = rs.front();
6595 define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
6599 Editor::use_range_as_bar ()
6601 framepos_t start, end;
6602 if (get_edit_op_range (start, end)) {
6603 define_one_bar (start, end);
6608 Editor::define_one_bar (framepos_t start, framepos_t end)
6610 framepos_t length = end - start;
6612 const Meter& m (_session->tempo_map().meter_at_frame (start));
6614 /* length = 1 bar */
6616 /* We're going to deliver a constant tempo here,
6617 so we can use frames per beat to determine length.
6618 now we want frames per beat.
6619 we have frames per bar, and beats per bar, so ...
6622 /* XXXX METER MATH */
6624 double frames_per_beat = length / m.divisions_per_bar();
6626 /* beats per minute = */
6628 double beats_per_minute = (_session->frame_rate() * 60.0) / frames_per_beat;
6630 /* now decide whether to:
6632 (a) set global tempo
6633 (b) add a new tempo marker
6637 const TempoSection& t (_session->tempo_map().tempo_section_at_frame (start));
6639 bool do_global = false;
6641 if ((_session->tempo_map().n_tempos() == 1) && (_session->tempo_map().n_meters() == 1)) {
6643 /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
6644 at the start, or create a new marker
6647 vector<string> options;
6648 options.push_back (_("Cancel"));
6649 options.push_back (_("Add new marker"));
6650 options.push_back (_("Set global tempo"));
6653 _("Define one bar"),
6654 _("Do you want to set the global tempo or add a new tempo marker?"),
6658 c.set_default_response (2);
6674 /* more than 1 tempo and/or meter section already, go ahead do the "usual":
6675 if the marker is at the region starter, change it, otherwise add
6680 begin_reversible_command (_("set tempo from region"));
6681 XMLNode& before (_session->tempo_map().get_state());
6684 _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
6685 } else if (t.frame() == start) {
6686 _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
6688 const Tempo tempo (beats_per_minute, t.note_type());
6689 _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
6692 XMLNode& after (_session->tempo_map().get_state());
6694 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
6695 commit_reversible_command ();
6699 Editor::split_region_at_transients ()
6701 AnalysisFeatureList positions;
6703 RegionSelection rs = get_regions_from_selection_and_entered ();
6705 if (!_session || rs.empty()) {
6709 begin_reversible_command (_("split regions"));
6711 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ) {
6713 RegionSelection::iterator tmp;
6718 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
6721 ar->transients (positions);
6722 split_region_at_points ((*i)->region(), positions, true);
6729 commit_reversible_command ();
6734 Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions, bool can_ferret, bool select_new)
6736 bool use_rhythmic_rodent = false;
6738 boost::shared_ptr<Playlist> pl = r->playlist();
6740 list<boost::shared_ptr<Region> > new_regions;
6746 if (positions.empty()) {
6750 if (positions.size() > 20 && can_ferret) {
6751 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);
6752 MessageDialog msg (msgstr,
6755 Gtk::BUTTONS_OK_CANCEL);
6758 msg.add_button (_("Call for the Ferret!"), RESPONSE_APPLY);
6759 msg.set_secondary_text (_("Press OK to continue with this split operation\nor ask the Ferret dialog to tune the analysis"));
6761 msg.set_secondary_text (_("Press OK to continue with this split operation"));
6764 msg.set_title (_("Excessive split?"));
6767 int response = msg.run();
6773 case RESPONSE_APPLY:
6774 use_rhythmic_rodent = true;
6781 if (use_rhythmic_rodent) {
6782 show_rhythm_ferret ();
6786 AnalysisFeatureList::const_iterator x;
6788 pl->clear_changes ();
6789 pl->clear_owned_changes ();
6791 x = positions.begin();
6793 if (x == positions.end()) {
6798 pl->remove_region (r);
6802 framepos_t rstart = r->first_frame ();
6803 framepos_t rend = r->last_frame ();
6805 while (x != positions.end()) {
6807 /* deal with positons that are out of scope of present region bounds */
6808 if (*x <= rstart || *x > rend) {
6813 /* file start = original start + how far we from the initial position ? */
6815 framepos_t file_start = r->start() + pos;
6817 /* length = next position - current position */
6819 framepos_t len = (*x) - pos - rstart;
6821 /* XXX we do we really want to allow even single-sample regions?
6822 * shouldn't we have some kind of lower limit on region size?
6831 if (RegionFactory::region_name (new_name, r->name())) {
6835 /* do NOT announce new regions 1 by one, just wait till they are all done */
6839 plist.add (ARDOUR::Properties::start, file_start);
6840 plist.add (ARDOUR::Properties::length, len);
6841 plist.add (ARDOUR::Properties::name, new_name);
6842 plist.add (ARDOUR::Properties::layer, 0);
6843 // TODO set transients_offset
6845 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6846 /* because we set annouce to false, manually add the new region to the
6849 RegionFactory::map_add (nr);
6851 pl->add_region (nr, rstart + pos);
6854 new_regions.push_front(nr);
6863 RegionFactory::region_name (new_name, r->name());
6865 /* Add the final region */
6868 plist.add (ARDOUR::Properties::start, r->start() + pos);
6869 plist.add (ARDOUR::Properties::length, r->last_frame() - (r->position() + pos) + 1);
6870 plist.add (ARDOUR::Properties::name, new_name);
6871 plist.add (ARDOUR::Properties::layer, 0);
6873 boost::shared_ptr<Region> nr = RegionFactory::create (r->sources(), plist, false);
6874 /* because we set annouce to false, manually add the new region to the
6877 RegionFactory::map_add (nr);
6878 pl->add_region (nr, r->position() + pos);
6881 new_regions.push_front(nr);
6886 /* We might have removed regions, which alters other regions' layering_index,
6887 so we need to do a recursive diff here.
6889 vector<Command*> cmds;
6891 _session->add_commands (cmds);
6893 _session->add_command (new StatefulDiffCommand (pl));
6897 for (list<boost::shared_ptr<Region> >::iterator i = new_regions.begin(); i != new_regions.end(); ++i){
6898 set_selected_regionview_from_region_list ((*i), Selection::Add);
6904 Editor::place_transient()
6910 RegionSelection rs = get_regions_from_selection_and_edit_point ();
6916 framepos_t where = get_preferred_edit_position();
6918 begin_reversible_command (_("place transient"));
6920 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6921 (*r)->region()->add_transient(where);
6924 commit_reversible_command ();
6928 Editor::remove_transient(ArdourCanvas::Item* item)
6934 ArdourCanvas::Line* _line = reinterpret_cast<ArdourCanvas::Line*> (item);
6937 AudioRegionView* _arv = reinterpret_cast<AudioRegionView*> (item->get_data ("regionview"));
6938 _arv->remove_transient (*(float*) _line->get_data ("position"));
6942 Editor::snap_regions_to_grid ()
6944 list <boost::shared_ptr<Playlist > > used_playlists;
6946 RegionSelection rs = get_regions_from_selection_and_entered ();
6948 if (!_session || rs.empty()) {
6952 begin_reversible_command (_("snap regions to grid"));
6954 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
6956 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
6958 if (!pl->frozen()) {
6959 /* we haven't seen this playlist before */
6961 /* remember used playlists so we can thaw them later */
6962 used_playlists.push_back(pl);
6966 framepos_t start_frame = (*r)->region()->first_frame ();
6967 snap_to (start_frame);
6968 (*r)->region()->set_position (start_frame);
6971 while (used_playlists.size() > 0) {
6972 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
6974 used_playlists.pop_front();
6977 commit_reversible_command ();
6981 Editor::close_region_gaps ()
6983 list <boost::shared_ptr<Playlist > > used_playlists;
6985 RegionSelection rs = get_regions_from_selection_and_entered ();
6987 if (!_session || rs.empty()) {
6991 Dialog dialog (_("Close Region Gaps"));
6994 table.set_spacings (12);
6995 table.set_border_width (12);
6996 Label* l = manage (left_aligned_label (_("Crossfade length")));
6997 table.attach (*l, 0, 1, 0, 1);
6999 SpinButton spin_crossfade (1, 0);
7000 spin_crossfade.set_range (0, 15);
7001 spin_crossfade.set_increments (1, 1);
7002 spin_crossfade.set_value (5);
7003 table.attach (spin_crossfade, 1, 2, 0, 1);
7005 table.attach (*manage (new Label (_("ms"))), 2, 3, 0, 1);
7007 l = manage (left_aligned_label (_("Pull-back length")));
7008 table.attach (*l, 0, 1, 1, 2);
7010 SpinButton spin_pullback (1, 0);
7011 spin_pullback.set_range (0, 100);
7012 spin_pullback.set_increments (1, 1);
7013 spin_pullback.set_value(30);
7014 table.attach (spin_pullback, 1, 2, 1, 2);
7016 table.attach (*manage (new Label (_("ms"))), 2, 3, 1, 2);
7018 dialog.get_vbox()->pack_start (table);
7019 dialog.add_button (Stock::CANCEL, RESPONSE_CANCEL);
7020 dialog.add_button (_("Ok"), RESPONSE_ACCEPT);
7023 if (dialog.run () == RESPONSE_CANCEL) {
7027 framepos_t crossfade_len = spin_crossfade.get_value();
7028 framepos_t pull_back_frames = spin_pullback.get_value();
7030 crossfade_len = lrintf (crossfade_len * _session->frame_rate()/1000);
7031 pull_back_frames = lrintf (pull_back_frames * _session->frame_rate()/1000);
7033 /* Iterate over the region list and make adjacent regions overlap by crossfade_len_ms */
7035 begin_reversible_command (_("close region gaps"));
7038 boost::shared_ptr<Region> last_region;
7040 rs.sort_by_position_and_track();
7042 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7044 boost::shared_ptr<Playlist> pl = (*r)->region()->playlist();
7046 if (!pl->frozen()) {
7047 /* we haven't seen this playlist before */
7049 /* remember used playlists so we can thaw them later */
7050 used_playlists.push_back(pl);
7054 framepos_t position = (*r)->region()->position();
7056 if (idx == 0 || position < last_region->position()){
7057 last_region = (*r)->region();
7062 (*r)->region()->trim_front( (position - pull_back_frames));
7063 last_region->trim_end( (position - pull_back_frames + crossfade_len));
7065 last_region = (*r)->region();
7070 while (used_playlists.size() > 0) {
7071 list <boost::shared_ptr<Playlist > >::iterator i = used_playlists.begin();
7073 used_playlists.pop_front();
7076 commit_reversible_command ();
7080 Editor::tab_to_transient (bool forward)
7082 AnalysisFeatureList positions;
7084 RegionSelection rs = get_regions_from_selection_and_entered ();
7090 framepos_t pos = _session->audible_frame ();
7092 if (!selection->tracks.empty()) {
7094 /* don't waste time searching for transients in duplicate playlists.
7097 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7099 for (TrackViewList::iterator t = ts.begin(); t != ts.end(); ++t) {
7101 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
7104 boost::shared_ptr<Track> tr = rtv->track();
7106 boost::shared_ptr<Playlist> pl = tr->playlist ();
7108 framepos_t result = pl->find_next_transient (pos, forward ? 1 : -1);
7111 positions.push_back (result);
7124 for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
7125 (*r)->region()->get_transients (positions);
7129 TransientDetector::cleanup_transients (positions, _session->frame_rate(), 3.0);
7132 AnalysisFeatureList::iterator x;
7134 for (x = positions.begin(); x != positions.end(); ++x) {
7140 if (x != positions.end ()) {
7141 _session->request_locate (*x);
7145 AnalysisFeatureList::reverse_iterator x;
7147 for (x = positions.rbegin(); x != positions.rend(); ++x) {
7153 if (x != positions.rend ()) {
7154 _session->request_locate (*x);
7160 Editor::playhead_forward_to_grid ()
7166 framepos_t pos = playhead_cursor->current_frame ();
7167 if (pos < max_framepos - 1) {
7169 snap_to_internal (pos, RoundUpAlways, false);
7170 _session->request_locate (pos);
7176 Editor::playhead_backward_to_grid ()
7182 framepos_t pos = playhead_cursor->current_frame ();
7185 snap_to_internal (pos, RoundDownAlways, false);
7186 _session->request_locate (pos);
7191 Editor::set_track_height (Height h)
7193 TrackSelection& ts (selection->tracks);
7195 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7196 (*x)->set_height_enum (h);
7201 Editor::toggle_tracks_active ()
7203 TrackSelection& ts (selection->tracks);
7205 bool target = false;
7211 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7212 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*x);
7216 target = !rtv->_route->active();
7219 rtv->_route->set_active (target, this);
7225 Editor::remove_tracks ()
7227 /* this will delete GUI objects that may be the subject of an event
7228 handler in which this method is called. Defer actual deletion to the
7229 next idle callback, when all event handling is finished.
7231 Glib::signal_idle().connect (sigc::mem_fun (*this, &Editor::idle_remove_tracks));
7235 Editor::idle_remove_tracks ()
7237 Session::StateProtector sp (_session);
7239 return false; /* do not call again */
7243 Editor::_remove_tracks ()
7245 TrackSelection& ts (selection->tracks);
7251 vector<string> choices;
7255 const char* trackstr;
7257 vector<boost::shared_ptr<Route> > routes;
7258 bool special_bus = false;
7260 for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
7261 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
7265 if (rtv->is_track()) {
7270 routes.push_back (rtv->_route);
7272 if (rtv->route()->is_master() || rtv->route()->is_monitor()) {
7277 if (special_bus && !Config->get_allow_special_bus_removal()) {
7278 MessageDialog msg (_("That would be bad news ...."),
7282 msg.set_secondary_text (string_compose (_(
7283 "Removing the master or monitor bus is such a bad idea\n\
7284 that %1 is not going to allow it.\n\
7286 If you really want to do this sort of thing\n\
7287 edit your ardour.rc file to set the\n\
7288 \"allow-special-bus-removal\" option to be \"yes\""), PROGRAM_NAME));
7295 if (ntracks + nbusses == 0) {
7299 trackstr = P_("track", "tracks", ntracks);
7300 busstr = P_("bus", "busses", nbusses);
7304 prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
7305 "(You may also lose the playlists associated with the %2)\n\n"
7306 "This action cannot be undone, and the session file will be overwritten!"),
7307 ntracks, trackstr, nbusses, busstr);
7309 prompt = string_compose (_("Do you really want to remove %1 %2?\n"
7310 "(You may also lose the playlists associated with the %2)\n\n"
7311 "This action cannot be undone, and the session file will be overwritten!"),
7314 } else if (nbusses) {
7315 prompt = string_compose (_("Do you really want to remove %1 %2?\n\n"
7316 "This action cannot be undone, and the session file will be overwritten"),
7320 choices.push_back (_("No, do nothing."));
7321 if (ntracks + nbusses > 1) {
7322 choices.push_back (_("Yes, remove them."));
7324 choices.push_back (_("Yes, remove it."));
7329 title = string_compose (_("Remove %1"), trackstr);
7331 title = string_compose (_("Remove %1"), busstr);
7334 Choice prompter (title, prompt, choices);
7336 if (prompter.run () != 1) {
7340 if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
7341 /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
7342 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
7343 * likely because deletion requires selection) this will call
7344 * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
7345 * It's likewise likely that the route that has just been displayed in the
7346 * Editor-Mixer will be next in line for deletion.
7348 * So simply switch to the master-bus (if present)
7350 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7351 if ((*i)->stripable ()->is_master ()) {
7352 set_selected_mixer_strip (*(*i));
7358 Mixer_UI::instance()->selection().block_routes_changed (true);
7359 selection->block_tracks_changed (true);
7361 DisplaySuspender ds;
7362 boost::shared_ptr<RouteList> rl (new RouteList);
7363 for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
7366 _session->remove_routes (rl);
7368 /* TrackSelection and RouteList leave scope,
7369 * destructors are called,
7370 * diskstream drops references, save_state is called (again for every track)
7372 selection->block_tracks_changed (false);
7373 Mixer_UI::instance()->selection().block_routes_changed (false);
7374 selection->TracksChanged (); /* EMIT SIGNAL */
7378 Editor::do_insert_time ()
7380 if (selection->tracks.empty()) {
7384 InsertRemoveTimeDialog d (*this);
7385 int response = d.run ();
7387 if (response != RESPONSE_OK) {
7391 if (d.distance() == 0) {
7398 d.intersected_region_action (),
7402 d.move_glued_markers(),
7403 d.move_locked_markers(),
7409 Editor::insert_time (
7410 framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7411 bool all_playlists, bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too
7415 if (Config->get_edit_mode() == Lock) {
7418 bool in_command = false;
7420 TrackViewList ts = selection->tracks.filter_to_unique_playlists ();
7422 for (TrackViewList::iterator x = ts.begin(); x != ts.end(); ++x) {
7426 /* don't operate on any playlist more than once, which could
7427 * happen if "all playlists" is enabled, but there is more
7428 * than 1 track using playlists "from" a given track.
7431 set<boost::shared_ptr<Playlist> > pl;
7433 if (all_playlists) {
7434 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7435 if (rtav && rtav->track ()) {
7436 vector<boost::shared_ptr<Playlist> > all = _session->playlists->playlists_for_track (rtav->track ());
7437 for (vector<boost::shared_ptr<Playlist> >::iterator p = all.begin(); p != all.end(); ++p) {
7442 if ((*x)->playlist ()) {
7443 pl.insert ((*x)->playlist ());
7447 for (set<boost::shared_ptr<Playlist> >::iterator i = pl.begin(); i != pl.end(); ++i) {
7449 (*i)->clear_changes ();
7450 (*i)->clear_owned_changes ();
7452 if (opt == SplitIntersected) {
7453 /* non musical split */
7454 (*i)->split (pos, 0);
7457 (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
7460 begin_reversible_command (_("insert time"));
7463 vector<Command*> cmds;
7465 _session->add_commands (cmds);
7467 _session->add_command (new StatefulDiffCommand (*i));
7471 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7474 begin_reversible_command (_("insert time"));
7477 rtav->route ()->shift (pos, frames);
7484 XMLNode& before (_session->locations()->get_state());
7485 Locations::LocationList copy (_session->locations()->list());
7487 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7489 Locations::LocationList::const_iterator tmp;
7491 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7492 bool const was_locked = (*i)->locked ();
7493 if (locked_markers_too) {
7497 if ((*i)->start() >= pos) {
7498 // move end first, in case we're moving by more than the length of the range
7499 if (!(*i)->is_mark()) {
7500 (*i)->set_end ((*i)->end() + frames);
7502 (*i)->set_start ((*i)->start() + frames);
7514 begin_reversible_command (_("insert time"));
7517 XMLNode& after (_session->locations()->get_state());
7518 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7524 begin_reversible_command (_("insert time"));
7527 XMLNode& before (_session->tempo_map().get_state());
7528 _session->tempo_map().insert_time (pos, frames);
7529 XMLNode& after (_session->tempo_map().get_state());
7530 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7534 commit_reversible_command ();
7539 Editor::do_remove_time ()
7541 if (selection->tracks.empty()) {
7545 InsertRemoveTimeDialog d (*this, true);
7547 int response = d.run ();
7549 if (response != RESPONSE_OK) {
7553 framecnt_t distance = d.distance();
7555 if (distance == 0) {
7565 d.move_glued_markers(),
7566 d.move_locked_markers(),
7572 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
7573 bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
7575 if (Config->get_edit_mode() == Lock) {
7576 error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;
7579 bool in_command = false;
7581 for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) {
7583 boost::shared_ptr<Playlist> pl = (*x)->playlist();
7587 XMLNode &before = pl->get_state();
7589 std::list<AudioRange> rl;
7590 AudioRange ar(pos, pos+frames, 0);
7593 pl->shift (pos, -frames, true, ignore_music_glue);
7596 begin_reversible_command (_("remove time"));
7599 XMLNode &after = pl->get_state();
7601 _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
7605 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (*x);
7608 begin_reversible_command (_("remove time"));
7611 rtav->route ()->shift (pos, -frames);
7615 std::list<Location*> loc_kill_list;
7620 XMLNode& before (_session->locations()->get_state());
7621 Locations::LocationList copy (_session->locations()->list());
7623 for (Locations::LocationList::iterator i = copy.begin(); i != copy.end(); ++i) {
7624 if ((*i)->position_lock_style() == AudioTime || glued_markers_too) {
7626 bool const was_locked = (*i)->locked ();
7627 if (locked_markers_too) {
7631 if (!(*i)->is_mark()) { // it's a range; have to handle both start and end
7632 if ((*i)->end() >= pos
7633 && (*i)->end() < pos+frames
7634 && (*i)->start() >= pos
7635 && (*i)->end() < pos+frames) { // range is completely enclosed; kill it
7637 loc_kill_list.push_back(*i);
7638 } else { // only start or end is included, try to do the right thing
7639 // move start before moving end, to avoid trying to move the end to before the start
7640 // if we're removing more time than the length of the range
7641 if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
7642 // start is within cut
7643 (*i)->set_start (pos); // bring the start marker to the beginning of the cut
7645 } else if ((*i)->start() >= pos+frames) {
7646 // start (and thus entire range) lies beyond end of cut
7647 (*i)->set_start ((*i)->start() - frames); // slip the start marker back
7650 if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
7651 // end is inside cut
7652 (*i)->set_end (pos); // bring the end to the cut
7654 } else if ((*i)->end() >= pos+frames) {
7655 // end is beyond end of cut
7656 (*i)->set_end ((*i)->end() - frames); // slip the end marker back
7661 } else if ((*i)->start() >= pos && (*i)->start() < pos+frames ) {
7662 loc_kill_list.push_back(*i);
7664 } else if ((*i)->start() >= pos) {
7665 (*i)->set_start ((*i)->start() -frames);
7675 for (list<Location*>::iterator i = loc_kill_list.begin(); i != loc_kill_list.end(); ++i) {
7676 _session->locations()->remove( *i );
7681 begin_reversible_command (_("remove time"));
7684 XMLNode& after (_session->locations()->get_state());
7685 _session->add_command (new MementoCommand<Locations>(*_session->locations(), &before, &after));
7690 XMLNode& before (_session->tempo_map().get_state());
7692 if (_session->tempo_map().remove_time (pos, frames) ) {
7694 begin_reversible_command (_("remove time"));
7697 XMLNode& after (_session->tempo_map().get_state());
7698 _session->add_command (new MementoCommand<TempoMap>(_session->tempo_map(), &before, &after));
7703 commit_reversible_command ();
7708 Editor::fit_selection ()
7710 if (!selection->tracks.empty()) {
7711 fit_tracks (selection->tracks);
7715 /* no selected tracks - use tracks with selected regions */
7717 if (!selection->regions.empty()) {
7718 for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
7719 tvl.push_back (&(*r)->get_time_axis_view ());
7725 } else if (internal_editing()) {
7726 /* no selected tracks, or regions, but in internal edit mode, so follow the mouse and use
7729 if (entered_track) {
7730 tvl.push_back (entered_track);
7739 Editor::fit_tracks (TrackViewList & tracks)
7741 if (tracks.empty()) {
7745 uint32_t child_heights = 0;
7746 int visible_tracks = 0;
7748 for (TrackSelection::iterator t = tracks.begin(); t != tracks.end(); ++t) {
7750 if (!(*t)->marked_for_display()) {
7754 child_heights += (*t)->effective_height() - (*t)->current_height();
7758 /* compute the per-track height from:
7760 total canvas visible height -
7761 height that will be taken by visible children of selected
7762 tracks - height of the ruler/hscroll area
7764 uint32_t h = (uint32_t) floor ((trackviews_height() - child_heights) / visible_tracks);
7765 double first_y_pos = DBL_MAX;
7767 if (h < TimeAxisView::preset_height (HeightSmall)) {
7768 MessageDialog msg (_("There are too many tracks to fit in the current window"));
7769 /* too small to be displayed */
7773 undo_visual_stack.push_back (current_visual_state (true));
7774 PBD::Unwinder<bool> nsv (no_save_visual, true);
7776 /* build a list of all tracks, including children */
7779 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
7781 TimeAxisView::Children c = (*i)->get_child_list ();
7782 for (TimeAxisView::Children::iterator j = c.begin(); j != c.end(); ++j) {
7783 all.push_back (j->get());
7788 // find selection range.
7789 // if someone knows how to user TrackViewList::iterator for this
7791 int selected_top = -1;
7792 int selected_bottom = -1;
7794 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7795 if ((*t)->marked_for_display ()) {
7796 if (tracks.contains(*t)) {
7797 if (selected_top == -1) {
7800 selected_bottom = i;
7806 for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t, ++i) {
7807 if ((*t)->marked_for_display ()) {
7808 if (tracks.contains(*t)) {
7809 (*t)->set_height (h);
7810 first_y_pos = std::min ((*t)->y_position (), first_y_pos);
7812 if (i > selected_top && i < selected_bottom) {
7813 hide_track_in_display (*t);
7820 set the controls_layout height now, because waiting for its size
7821 request signal handler will cause the vertical adjustment setting to fail
7824 controls_layout.property_height () = _full_canvas_height;
7825 vertical_adjustment.set_value (first_y_pos);
7827 redo_visual_stack.push_back (current_visual_state (true));
7829 visible_tracks_selector.set_text (_("Sel"));
7833 Editor::save_visual_state (uint32_t n)
7835 while (visual_states.size() <= n) {
7836 visual_states.push_back (0);
7839 if (visual_states[n] != 0) {
7840 delete visual_states[n];
7843 visual_states[n] = current_visual_state (true);
7848 Editor::goto_visual_state (uint32_t n)
7850 if (visual_states.size() <= n) {
7854 if (visual_states[n] == 0) {
7858 use_visual_state (*visual_states[n]);
7862 Editor::start_visual_state_op (uint32_t n)
7864 save_visual_state (n);
7866 PopUp* pup = new PopUp (WIN_POS_MOUSE, 1000, true);
7868 snprintf (buf, sizeof (buf), _("Saved view %u"), n+1);
7869 pup->set_text (buf);
7874 Editor::cancel_visual_state_op (uint32_t n)
7876 goto_visual_state (n);
7880 Editor::toggle_region_mute ()
7882 if (_ignore_region_action) {
7886 RegionSelection rs = get_regions_from_selection_and_entered ();
7892 if (rs.size() > 1) {
7893 begin_reversible_command (_("mute regions"));
7895 begin_reversible_command (_("mute region"));
7898 for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
7900 (*i)->region()->playlist()->clear_changes ();
7901 (*i)->region()->set_muted (!(*i)->region()->muted ());
7902 _session->add_command (new StatefulDiffCommand ((*i)->region()));
7906 commit_reversible_command ();
7910 Editor::combine_regions ()
7912 /* foreach track with selected regions, take all selected regions
7913 and join them into a new region containing the subregions (as a
7917 typedef set<RouteTimeAxisView*> RTVS;
7920 if (selection->regions.empty()) {
7924 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7925 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7928 tracks.insert (rtv);
7932 begin_reversible_command (_("combine regions"));
7934 vector<RegionView*> new_selection;
7936 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7939 if ((rv = (*i)->combine_regions ()) != 0) {
7940 new_selection.push_back (rv);
7944 selection->clear_regions ();
7945 for (vector<RegionView*>::iterator i = new_selection.begin(); i != new_selection.end(); ++i) {
7946 selection->add (*i);
7949 commit_reversible_command ();
7953 Editor::uncombine_regions ()
7955 typedef set<RouteTimeAxisView*> RTVS;
7958 if (selection->regions.empty()) {
7962 for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
7963 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&(*i)->get_time_axis_view());
7966 tracks.insert (rtv);
7970 begin_reversible_command (_("uncombine regions"));
7972 for (RTVS::iterator i = tracks.begin(); i != tracks.end(); ++i) {
7973 (*i)->uncombine_regions ();
7976 commit_reversible_command ();
7980 Editor::toggle_midi_input_active (bool flip_others)
7983 boost::shared_ptr<RouteList> rl (new RouteList);
7985 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
7986 RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
7992 boost::shared_ptr<MidiTrack> mt = rtav->midi_track();
7995 rl->push_back (rtav->route());
7996 onoff = !mt->input_active();
8000 _session->set_exclusive_input_active (rl, onoff, flip_others);
8003 static bool ok_fine (GdkEventAny*) { return true; }
8009 lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
8011 Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
8012 lock_dialog->get_vbox()->pack_start (*padlock);
8013 lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
8015 ArdourButton* b = manage (new ArdourButton);
8016 b->set_name ("lock button");
8017 b->set_text (_("Click to unlock"));
8018 b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
8019 lock_dialog->get_vbox()->pack_start (*b);
8021 lock_dialog->get_vbox()->show_all ();
8022 lock_dialog->set_size_request (200, 200);
8025 delete _main_menu_disabler;
8026 _main_menu_disabler = new MainMenuDisabler;
8028 lock_dialog->present ();
8030 lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
8036 lock_dialog->hide ();
8038 delete _main_menu_disabler;
8039 _main_menu_disabler = 0;
8041 if (UIConfiguration::instance().get_lock_gui_after_seconds()) {
8042 start_lock_event_timing ();
8047 Editor::bring_in_callback (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8049 Gtkmm2ext::UI::instance()->call_slot (invalidator (*this), boost::bind (&Editor::update_bring_in_message, this, label, n, total, name));
8053 Editor::update_bring_in_message (Gtk::Label* label, uint32_t n, uint32_t total, string name)
8055 Timers::TimerSuspender t;
8056 label->set_text (string_compose ("Copying %1, %2 of %3", name, n, total));
8057 Gtkmm2ext::UI::instance()->flush_pending (1);
8061 Editor::bring_all_sources_into_session ()
8068 ArdourDialog w (_("Moving embedded files into session folder"));
8069 w.get_vbox()->pack_start (msg);
8072 /* flush all pending GUI events because we're about to start copying
8076 Timers::TimerSuspender t;
8077 Gtkmm2ext::UI::instance()->flush_pending (3);
8081 _session->bring_all_sources_into_session (boost::bind (&Editor::bring_in_callback, this, &msg, _1, _2, _3));